PasTools.pas 18 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675676677678679
  1. unit PasTools;
  2. interface
  3. uses
  4. Windows, Types, Classes, ComCtrls, ExtCtrls, Controls, Dialogs, Forms;
  5. function Construct(ComponentClass: TComponentClass; Owner: TComponent): TComponent;
  6. function IsVistaHard: Boolean;
  7. function IsVista: Boolean;
  8. function IsWin7: Boolean;
  9. function IsWin8: Boolean;
  10. function CutToChar(var Str: string; Ch: Char; Trim: Boolean): string;
  11. procedure FilterToFileTypes(Filter: string; FileTypes: TFileTypeItems);
  12. // Note that while we based our scaling on pixels-per-inch,
  13. // VCL actually scales based on font size
  14. function LoadDimension(Dimension: Integer; PixelsPerInch: Integer): Integer;
  15. function StrToDimensionDef(Str: string; PixelsPerInch: Integer; Default: Integer): Integer;
  16. function SaveDimension(Dimension: Integer): Integer;
  17. function DimensionToDefaultPixelsPerInch(Dimension: Integer): Integer;
  18. function LoadPixelsPerInch(S: string): Integer;
  19. function SavePixelsPerInch: string;
  20. function SaveDefaultPixelsPerInch: string;
  21. function ScaleByTextHeight(Control: TControl; Dimension: Integer): Integer;
  22. function ScaleByTextHeightRunTime(Control: TControl; Dimension: Integer): Integer;
  23. function ControlHasRecreationPersistenceData(Control: TControl): Boolean;
  24. function IsAppIconic: Boolean;
  25. procedure SetAppIconic(Value: Boolean);
  26. procedure SetAppMainForm(Value: TForm);
  27. procedure ForceColorChange(Control: TWinControl);
  28. type
  29. TApiPathEvent = function(Path: string): string;
  30. var
  31. OnApiPath: TApiPathEvent = nil;
  32. function ApiPath(Path: string): string;
  33. type
  34. TControlScrollBeforeUpdate = procedure(ObjectToValidate: TObject) of object;
  35. TControlScrollAfterUpdate = procedure of object;
  36. TCustomControlScrollOnDragOver = class
  37. private
  38. FOnBeforeUpdate: TControlScrollBeforeUpdate;
  39. FOnAfterUpdate: TControlScrollAfterUpdate;
  40. FDragOverTimer: TTimer;
  41. FControl: TControl;
  42. FDragOverTime: FILETIME;
  43. FLastVScrollTime: FILETIME;
  44. FVScrollCount: Integer;
  45. procedure DragOverTimer(Sender: TObject);
  46. procedure BeforeUpdate(ObjectToValidate: TObject);
  47. procedure AfterUpdate;
  48. public
  49. constructor Create(Control: TControl; ScheduleDragOver: Boolean);
  50. destructor Destroy; override;
  51. procedure StartDrag; virtual;
  52. procedure EndDrag; virtual;
  53. procedure DragOver(Point: TPoint); virtual; abstract;
  54. property OnBeforeUpdate: TControlScrollBeforeUpdate read FOnBeforeUpdate write FOnBeforeUpdate;
  55. property OnAfterUpdate: TControlScrollAfterUpdate read FOnAfterUpdate write FOnAfterUpdate;
  56. end;
  57. TTreeViewScrollOnDragOver = class(TCustomControlScrollOnDragOver)
  58. private
  59. FLastDragNode: TTreeNode;
  60. FLastHScrollTime: FILETIME;
  61. public
  62. procedure StartDrag; override;
  63. procedure DragOver(Point: TPoint); override;
  64. end;
  65. TListViewScrollOnDragOver = class(TCustomControlScrollOnDragOver)
  66. public
  67. procedure DragOver(Point: TPoint); override;
  68. end;
  69. TListBoxScrollOnDragOver = class(TCustomControlScrollOnDragOver)
  70. public
  71. procedure DragOver(Point: TPoint); override;
  72. end;
  73. implementation
  74. uses
  75. SysUtils, Messages, StdCtrls, Graphics;
  76. const
  77. DDExpandDelay = 15000000;
  78. DDMaxSlowCount = 3;
  79. DDVScrollDelay = 2000000;
  80. DDHScrollDelay = 2000000;
  81. DDDragStartDelay = 500000;
  82. function Construct(ComponentClass: TComponentClass; Owner: TComponent): TComponent;
  83. begin
  84. Result := ComponentClass.Create(Owner);
  85. end;
  86. // detects vista, even in compatibility mode
  87. // (GetLocaleInfoEx is available since Vista only)
  88. function IsVistaHard: Boolean;
  89. begin
  90. Result := (GetProcAddress(GetModuleHandle(Kernel32), 'GetLocaleInfoEx') <> nil);
  91. end;
  92. function IsVista: Boolean;
  93. begin
  94. Result := CheckWin32Version(6, 0);
  95. end;
  96. function IsWin7: Boolean;
  97. begin
  98. Result := CheckWin32Version(6, 1);
  99. end;
  100. function IsWin8: Boolean;
  101. begin
  102. Result := CheckWin32Version(6, 2);
  103. end;
  104. function CutToChar(var Str: string; Ch: Char; Trim: Boolean): string;
  105. var
  106. P: Integer;
  107. begin
  108. P := Pos(Ch, Str);
  109. if P > 0 then
  110. begin
  111. Result := Copy(Str, 1, P-1);
  112. Delete(Str, 1, P);
  113. end
  114. else
  115. begin
  116. Result := Str;
  117. Str := '';
  118. end;
  119. if Trim then Result := SysUtils.Trim(Result);
  120. end;
  121. procedure FilterToFileTypes(Filter: string; FileTypes: TFileTypeItems);
  122. var
  123. Item: TFileTypeItem;
  124. begin
  125. while Filter <> '' do
  126. begin
  127. Item := FileTypes.Add();
  128. Item.DisplayName := CutToChar(Filter, '|', True);
  129. Item.FileMask := CutToChar(Filter, '|', True);
  130. end;
  131. end;
  132. function LoadDimension(Dimension: Integer; PixelsPerInch: Integer): Integer;
  133. begin
  134. Result := MulDiv(Dimension, Screen.PixelsPerInch, PixelsPerInch);
  135. end;
  136. function StrToDimensionDef(Str: string; PixelsPerInch: Integer; Default: Integer): Integer;
  137. begin
  138. if TryStrToInt(Str, Result) then
  139. begin
  140. Result := LoadDimension(Result, PixelsPerInch);
  141. end
  142. else
  143. begin
  144. Result := Default;
  145. end;
  146. end;
  147. function SaveDimension(Dimension: Integer): Integer;
  148. begin
  149. // noop
  150. Result := Dimension;
  151. end;
  152. function DimensionToDefaultPixelsPerInch(Dimension: Integer): Integer;
  153. begin
  154. Result := MulDiv(Dimension, USER_DEFAULT_SCREEN_DPI, Screen.PixelsPerInch);
  155. end;
  156. function LoadPixelsPerInch(S: string): Integer;
  157. begin
  158. // for backward compatibility with version that did not save the DPI,
  159. // make reasonable assumption that the configuration was saved with
  160. // the same DPI as we run now
  161. Result := StrToIntDef(S, Screen.PixelsPerInch);
  162. end;
  163. function SavePixelsPerInch: string;
  164. begin
  165. Result := IntToStr(Screen.PixelsPerInch);
  166. end;
  167. function SaveDefaultPixelsPerInch: string;
  168. begin
  169. Result := IntToStr(USER_DEFAULT_SCREEN_DPI);
  170. end;
  171. // WORKAROUND
  172. // http://stackoverflow.com/questions/9410485/how-do-i-use-class-helpers-to-access-strict-private-members-of-a-class
  173. type
  174. TFormHelper = class helper for TCustomForm
  175. public
  176. function RetrieveTextHeight: Integer;
  177. function CalculateTextHeight: Integer;
  178. end;
  179. function TFormHelper.RetrieveTextHeight: Integer;
  180. begin
  181. Result := Self.FTextHeight;
  182. end;
  183. function TFormHelper.CalculateTextHeight: Integer;
  184. begin
  185. Result := Self.GetTextHeight;
  186. end;
  187. function ScaleByTextHeightImpl(Control: TControl; Dimension: Integer; TextHeight: Integer): Integer;
  188. var
  189. Form: TCustomForm;
  190. NewTextHeight: Integer;
  191. begin
  192. // RTL_COPY (TCustomForm.ReadState)
  193. Form := ValidParentForm(Control);
  194. NewTextHeight := Form.CalculateTextHeight;
  195. if TextHeight <> NewTextHeight then
  196. begin
  197. Dimension := MulDiv(Dimension, NewTextHeight, TextHeight);
  198. end;
  199. Result := Dimension;
  200. end;
  201. const
  202. OurDesignTimeTextHeight = 13;
  203. function ScaleByTextHeight(Control: TControl; Dimension: Integer): Integer;
  204. var
  205. Form: TCustomForm;
  206. TextHeight: Integer;
  207. begin
  208. // RTL_COPY (TCustomForm.ReadState)
  209. Form := ValidParentForm(Control);
  210. TextHeight := Form.RetrieveTextHeight;
  211. // that's our design text-size, we do not expect any other value
  212. Assert(TextHeight = OurDesignTimeTextHeight);
  213. Result := ScaleByTextHeightImpl(Control, Dimension, TextHeight);
  214. end;
  215. // this differs from ScaleByTextHeight only by enforcing
  216. // constant design-time text height
  217. function ScaleByTextHeightRunTime(Control: TControl; Dimension: Integer): Integer;
  218. begin
  219. Result := ScaleByTextHeightImpl(Control, Dimension, OurDesignTimeTextHeight);
  220. end;
  221. type
  222. TListViewHelper = class helper for TCustomListView
  223. public
  224. function HasMemStream: Boolean;
  225. end;
  226. function TListViewHelper.HasMemStream: Boolean;
  227. begin
  228. Result := Assigned(Self.FMemStream);
  229. end;
  230. type
  231. TTreeViewHelper = class helper for TCustomTreeView
  232. public
  233. function HasMemStream: Boolean;
  234. end;
  235. function TTreeViewHelper.HasMemStream: Boolean;
  236. begin
  237. Result := Assigned(Self.FMemStream);
  238. end;
  239. type
  240. TRichEditHelper = class helper for TCustomRichEdit
  241. public
  242. function HasMemStream: Boolean;
  243. end;
  244. function TRichEditHelper.HasMemStream: Boolean;
  245. begin
  246. Result := Assigned(Self.FMemStream);
  247. end;
  248. function ControlHasRecreationPersistenceData(Control: TControl): Boolean;
  249. begin
  250. // not implemented for this class as we do not use it as of now
  251. Assert(not (Control is TCustomComboBoxEx));
  252. Result :=
  253. ((Control is TCustomListView) and (Control as TCustomListView).HasMemStream) or
  254. ((Control is TCustomTreeView) and (Control as TCustomTreeView).HasMemStream) or
  255. ((Control is TCustomRichEdit) and (Control as TCustomRichEdit).HasMemStream);
  256. end;
  257. type
  258. TApplicationHelper = class helper for TApplication
  259. public
  260. function IsAppIconic: Boolean;
  261. procedure SetAppIconic(Value: Boolean);
  262. procedure SetMainForm(Value: TForm);
  263. end;
  264. function TApplicationHelper.IsAppIconic: Boolean;
  265. begin
  266. Result := Self.FAppIconic;
  267. end;
  268. procedure TApplicationHelper.SetAppIconic(Value: Boolean);
  269. begin
  270. Self.FAppIconic := Value;
  271. end;
  272. procedure TApplicationHelper.SetMainForm(Value: TForm);
  273. begin
  274. Self.FMainForm := Value;
  275. end;
  276. function IsAppIconic: Boolean;
  277. begin
  278. Result := Application.IsAppIconic;
  279. end;
  280. procedure SetAppIconic(Value: Boolean);
  281. begin
  282. Application.SetAppIconic(Value);
  283. end;
  284. procedure SetAppMainForm(Value: TForm);
  285. begin
  286. Application.SetMainForm(Value);
  287. end;
  288. function ApiPath(Path: string): string;
  289. begin
  290. Result := Path;
  291. if Assigned(OnApiPath) then
  292. begin
  293. Result := OnApiPath(Result);
  294. end;
  295. end;
  296. procedure ForceColorChange(Control: TWinControl);
  297. begin
  298. // particularly when changing color back to default (clWindow),
  299. // non-client area (border line) is not redrawn,
  300. // keeping previous color. force redraw here
  301. if Control.HandleAllocated then
  302. begin
  303. RedrawWindow(Control.Handle, nil, 0, RDW_INVALIDATE or RDW_FRAME);
  304. end;
  305. end;
  306. { TCustomControlScrollOnDragOver }
  307. constructor TCustomControlScrollOnDragOver.Create(Control: TControl;
  308. ScheduleDragOver: Boolean);
  309. begin
  310. FControl := Control;
  311. FOnBeforeUpdate := nil;
  312. FOnAfterUpdate := nil;
  313. if ScheduleDragOver then
  314. begin
  315. FDragOverTimer := TTimer.Create(Control);
  316. FDragOverTimer.Enabled := False;
  317. FDragOverTimer.Interval := 50;
  318. FDragOverTimer.OnTimer := DragOverTimer;
  319. end
  320. else FDragOverTimer := nil;
  321. end;
  322. destructor TCustomControlScrollOnDragOver.Destroy;
  323. begin
  324. FreeAndNil(FDragOverTimer);
  325. end;
  326. procedure TCustomControlScrollOnDragOver.DragOverTimer(Sender: TObject);
  327. var
  328. P: TPoint;
  329. begin
  330. P := FControl.ScreenToClient(Mouse.CursorPos);
  331. if (P.X >= 0) and (P.X < FControl.Width) and
  332. (P.Y >= 0) and (P.Y < FControl.Height) then
  333. begin
  334. DragOver(P);
  335. end;
  336. end;
  337. procedure TCustomControlScrollOnDragOver.StartDrag;
  338. begin
  339. GetSystemTimeAsFileTime(FDragOverTime);
  340. GetSystemTimeAsFileTime(FLastVScrollTime);
  341. FVScrollCount := 0;
  342. if Assigned(FDragOverTimer) then
  343. FDragOverTimer.Enabled := True;
  344. end;
  345. procedure TCustomControlScrollOnDragOver.EndDrag;
  346. begin
  347. if Assigned(FDragOverTimer) then
  348. FDragOverTimer.Enabled := False;
  349. end;
  350. type
  351. TPublicControl = class(TControl);
  352. procedure TCustomControlScrollOnDragOver.BeforeUpdate(ObjectToValidate: TObject);
  353. var
  354. DragImages: TDragImageList;
  355. begin
  356. if Assigned(FOnBeforeUpdate) then
  357. FOnBeforeUpdate(ObjectToValidate);
  358. DragImages := TPublicControl(FControl).GetDragImages;
  359. if Assigned(DragImages) then
  360. DragImages.HideDragImage;
  361. end;
  362. procedure TCustomControlScrollOnDragOver.AfterUpdate;
  363. var
  364. DragImages: TDragImageList;
  365. begin
  366. if Assigned(FOnAfterUpdate) then
  367. FOnAfterUpdate;
  368. DragImages := TPublicControl(FControl).GetDragImages;
  369. if Assigned(DragImages) then
  370. DragImages.ShowDragImage;
  371. end;
  372. procedure TTreeViewScrollOnDragOver.StartDrag;
  373. var
  374. KeyBoardState : TKeyBoardState;
  375. begin
  376. inherited;
  377. FLastDragNode := nil;
  378. if (GetKeyState(VK_SPACE) <> 0) and GetKeyboardState(KeyBoardState) then
  379. begin
  380. KeyBoardState[VK_SPACE] := 0;
  381. SetKeyBoardState(KeyBoardState);
  382. end;
  383. GetSystemTimeAsFileTime(FLastHScrollTime);
  384. end;
  385. { TTreeViewScrollOnDragOver }
  386. procedure TTreeViewScrollOnDragOver.DragOver(Point: TPoint);
  387. var
  388. TreeView: TCustomTreeView;
  389. NbPixels: Integer;
  390. KnowTime: TFileTime;
  391. Node: TTreeNode;
  392. TempTopItem: TTreeNode;
  393. ScrollInfo: TScrollInfo;
  394. KeyBoardState : TKeyBoardState;
  395. begin
  396. TreeView := (FControl as TCustomTreeView);
  397. Node := TreeView.GetNodeAt(Point.X, Point.Y);
  398. if Assigned(Node) then
  399. begin
  400. GetSystemTimeAsFileTime(KnowTime);
  401. if GetKeyState(VK_SPACE) = 0 then
  402. begin
  403. {Expand node after 2.5 seconds: }
  404. if not Assigned(FLastDragNode) or (FLastDragNode <> Node) then
  405. begin
  406. {not previous droptarget: start timer}
  407. GetSystemTimeAsFileTime(FDragOverTime);
  408. FLastDragNode := Node
  409. end
  410. else
  411. begin
  412. if ((Int64(KnowTime) - Int64(FDragOverTime)) > DDExpandDelay) then
  413. begin
  414. TempTopItem := TreeView.TopItem;
  415. BeforeUpdate(nil);
  416. Node.Expand(False);
  417. TreeView.TopItem := TempTopItem;
  418. TreeView.Update;
  419. AfterUpdate;
  420. FDragOverTime := KnowTime;
  421. end;
  422. end;
  423. end
  424. else
  425. begin
  426. {restart timer}
  427. GetSystemTimeAsFileTime(FDragOverTime);
  428. if GetKeyboardState(KeyBoardState) then
  429. begin
  430. KeyBoardState[VK_Space] := 0;
  431. SetKeyBoardState(KeyBoardState);
  432. end;
  433. TempTopItem := TreeView.TopItem;
  434. BeforeUpdate(Node);
  435. if Node.Expanded then
  436. begin
  437. if not TreeView.Selected.HasAsParent(Node) then
  438. Node.Collapse(False);
  439. end
  440. else Node.Expand(False);
  441. TreeView.TopItem := TempTopItem;
  442. TreeView.Update;
  443. AfterUpdate;
  444. end;
  445. NbPixels := Abs(TTreeView(FControl).Font.Height);
  446. {Vertical treescrolling:}
  447. if ((Int64(KnowTime) - Int64(FLastVScrollTime)) > DDVScrollDelay) or
  448. ((FVScrollCount > 3) and
  449. ((Int64(KnowTime) - Int64(FLastVScrollTime)) > (DDVScrollDelay Div 4))) then
  450. begin
  451. {Scroll tree up, if droptarget is topitem:}
  452. if Node = TreeView.TopItem then
  453. begin
  454. BeforeUpdate(nil);
  455. TreeView.Perform(WM_VSCROLL, SB_LINEUP, 0);
  456. AfterUpdate;
  457. GetSystemTimeAsFileTime(FLastVScrollTime);
  458. Inc(FVScrollCount);
  459. end
  460. else
  461. {Scroll tree down, if next visible item of droptarget is not visible:}
  462. begin
  463. if Point.Y + 3 * nbPixels > TreeView.Height then
  464. begin
  465. BeforeUpdate(nil);
  466. TreeView.Perform(WM_VSCROLL, SB_LINEDOWN, 0);
  467. AfterUpdate;
  468. GetSystemTimeAsFileTime(FLastVScrollTime);
  469. Inc(FVScrollCount);
  470. end
  471. else
  472. begin
  473. FVScrollCount := 0;
  474. end;
  475. end;
  476. end; {VScrollDelay}
  477. {Horizontal treescrolling:}
  478. {Scroll tree Left}
  479. if ((Int64(KnowTime) - Int64(FLastHScrollTime)) > DDHScrollDelay) then
  480. begin
  481. GetSystemTimeAsFileTime(FLastHScrollTime);
  482. ScrollInfo.cbSize := SizeOf(ScrollInfo);
  483. ScrollInfo.FMask := SIF_ALL;
  484. GetScrollInfo(TreeView.Handle, SB_HORZ, ScrollInfo);
  485. if ScrollInfo.nMin <> ScrollInfo.nMax then
  486. begin
  487. if Point.X < 50 then
  488. begin
  489. if Node.DisplayRect(True).Right + 50 < TreeView.Width then
  490. begin
  491. BeforeUpdate(nil);
  492. TreeView.Perform(WM_HSCROLL, SB_LINELEFT, 0);
  493. AfterUpdate;
  494. end;
  495. end
  496. else
  497. if Point.X > (TreeView.Width - 50) then
  498. begin
  499. if Node.DisplayRect(True).Left > 50 then
  500. begin
  501. BeforeUpdate(nil);
  502. TreeView.Perform(WM_HSCROLL, SB_LINERIGHT, 0);
  503. AfterUpdate;
  504. end;
  505. end;
  506. end;
  507. end;
  508. end;
  509. end;
  510. { TListViewScrollOnDragOver }
  511. procedure TListViewScrollOnDragOver.DragOver(Point: TPoint);
  512. var
  513. ListView: TCustomListView;
  514. KnowTime: TFileTime;
  515. NbPixels: Integer;
  516. WParam: LongInt;
  517. begin
  518. ListView := (FControl as TCustomListView);
  519. GetSystemTimeAsFileTime(KnowTime);
  520. NbPixels := Abs(TListView(ListView).Font.Height);
  521. {Vertical scrolling, if viewstyle = vsReport:}
  522. if (TListView(ListView).ViewStyle = vsReport) and Assigned(ListView.TopItem) and
  523. (((Int64(KnowTime) - Int64(FLastVScrollTime)) > DDVScrollDelay) or
  524. ((FVScrollCount > DDMaxSlowCount) and
  525. ((Int64(KnowTime) - Int64(FLastVScrollTime)) > (DDVScrollDelay div 4)))) then
  526. begin
  527. if (Point.Y - 3 * nbPixels <= 0) and (ListView.TopItem.Index > 0) then WParam := SB_LINEUP
  528. else
  529. if (Point.Y + 3 * nbPixels > ListView.Height) then WParam := SB_LINEDOWN
  530. else WParam := -1;
  531. if WParam >= 0 then
  532. begin
  533. BeforeUpdate(nil);
  534. ListView.Perform(WM_VSCROLL, WParam, 0);
  535. if FVScrollCount > DDMaxSlowCount then
  536. ListView.Perform(WM_VSCROLL, WParam, 0);
  537. if FVScrollCount > DDMaxSlowCount * 3 then
  538. ListView.Perform(WM_VSCROLL, WParam, 0);
  539. ListView.Update;
  540. AfterUpdate;
  541. GetSystemTimeAsFileTime(FLastVScrollTime);
  542. Inc(FVScrollCount);
  543. end
  544. else FVScrollCount := 0;
  545. end;
  546. end;
  547. { TListBoxScrollOnDragOver }
  548. procedure TListBoxScrollOnDragOver.DragOver(Point: TPoint);
  549. var
  550. ListBox: TListBox;
  551. KnowTime: TFileTime;
  552. NbPixels: Integer;
  553. WParam: LongInt;
  554. begin
  555. ListBox := (FControl as TListBox);
  556. GetSystemTimeAsFileTime(KnowTime);
  557. NbPixels := Abs(ListBox.Font.Height);
  558. {Vertical scrolling, if viewstyle = vsReport:}
  559. if (ListBox.Items.Count > 0) and
  560. (((Int64(KnowTime) - Int64(FLastVScrollTime)) > DDVScrollDelay) or
  561. ((FVScrollCount > DDMaxSlowCount) and
  562. ((Int64(KnowTime) - Int64(FLastVScrollTime)) > (DDVScrollDelay div 4)))) then
  563. begin
  564. if (Point.Y - 3 * nbPixels <= 0) and (ListBox.TopIndex > 0) then WParam := SB_LINEUP
  565. else
  566. if (Point.Y + 3 * nbPixels > ListBox.Height) then WParam := SB_LINEDOWN
  567. else WParam := -1;
  568. if WParam >= 0 then
  569. begin
  570. BeforeUpdate(nil);
  571. ListBox.Perform(WM_VSCROLL, WParam, 0);
  572. if FVScrollCount > DDMaxSlowCount then
  573. ListBox.Perform(WM_VSCROLL, WParam, 0);
  574. if FVScrollCount > DDMaxSlowCount * 3 then
  575. ListBox.Perform(WM_VSCROLL, WParam, 0);
  576. ListBox.Update;
  577. AfterUpdate;
  578. GetSystemTimeAsFileTime(FLastVScrollTime);
  579. Inc(FVScrollCount);
  580. end
  581. else FVScrollCount := 0;
  582. end;
  583. end;
  584. end.