PasTools.pas 11 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389
  1. unit PasTools;
  2. interface
  3. uses
  4. Windows, Types, Classes, ComCtrls, ExtCtrls, Controls;
  5. function Construct(ComponentClass: TComponentClass; Owner: TComponent): TComponent;
  6. function IsVista: Boolean;
  7. type
  8. TControlScrollBeforeUpdate = procedure(ObjectToValidate: TObject) of object;
  9. TControlScrollAfterUpdate = procedure of object;
  10. TCustomControlScrollOnDragOver = class
  11. private
  12. FOnBeforeUpdate: TControlScrollBeforeUpdate;
  13. FOnAfterUpdate: TControlScrollAfterUpdate;
  14. FDragOverTimer: TTimer;
  15. FControl: TControl;
  16. FDragOverTime: FILETIME;
  17. FLastVScrollTime: FILETIME;
  18. FVScrollCount: Integer;
  19. procedure DragOverTimer(Sender: TObject);
  20. procedure BeforeUpdate(ObjectToValidate: TObject);
  21. procedure AfterUpdate;
  22. public
  23. constructor Create(Control: TControl; ScheduleDragOver: Boolean);
  24. destructor Destroy; override;
  25. procedure StartDrag; virtual;
  26. procedure EndDrag; virtual;
  27. procedure DragOver(Point: TPoint); virtual; abstract;
  28. property OnBeforeUpdate: TControlScrollBeforeUpdate read FOnBeforeUpdate write FOnBeforeUpdate;
  29. property OnAfterUpdate: TControlScrollAfterUpdate read FOnAfterUpdate write FOnAfterUpdate;
  30. end;
  31. TTreeViewScrollOnDragOver = class(TCustomControlScrollOnDragOver)
  32. private
  33. FLastDragNode: TTreeNode;
  34. FLastHScrollTime: FILETIME;
  35. public
  36. procedure StartDrag; override;
  37. procedure DragOver(Point: TPoint); override;
  38. end;
  39. TListViewScrollOnDragOver = class(TCustomControlScrollOnDragOver)
  40. public
  41. procedure DragOver(Point: TPoint); override;
  42. end;
  43. TListBoxScrollOnDragOver = class(TCustomControlScrollOnDragOver)
  44. public
  45. procedure DragOver(Point: TPoint); override;
  46. end;
  47. implementation
  48. uses
  49. SysUtils, Messages, StdCtrls;
  50. const
  51. DDExpandDelay = 15000000;
  52. DDMaxSlowCount = 3;
  53. DDVScrollDelay = 2000000;
  54. DDHScrollDelay = 2000000;
  55. DDDragStartDelay = 500000;
  56. function Construct(ComponentClass: TComponentClass; Owner: TComponent): TComponent;
  57. begin
  58. Result := ComponentClass.Create(Owner);
  59. end;
  60. // detects vista, even in compatibility mode
  61. // (GetLocaleInfoEx is available since Vista only)
  62. function IsVista: Boolean;
  63. begin
  64. Result := (GetProcAddress(GetModuleHandle(Kernel32), 'GetLocaleInfoEx') <> nil);
  65. end;
  66. { TCustomControlScrollOnDragOver }
  67. constructor TCustomControlScrollOnDragOver.Create(Control: TControl;
  68. ScheduleDragOver: Boolean);
  69. begin
  70. FControl := Control;
  71. FOnBeforeUpdate := nil;
  72. FOnAfterUpdate := nil;
  73. if ScheduleDragOver then
  74. begin
  75. FDragOverTimer := TTimer.Create(Control);
  76. FDragOverTimer.Enabled := False;
  77. FDragOverTimer.Interval := 50;
  78. FDragOverTimer.OnTimer := DragOverTimer;
  79. end
  80. else FDragOverTimer := nil;
  81. end;
  82. destructor TCustomControlScrollOnDragOver.Destroy;
  83. begin
  84. FreeAndNil(FDragOverTimer);
  85. end;
  86. procedure TCustomControlScrollOnDragOver.DragOverTimer(Sender: TObject);
  87. var
  88. P: TPoint;
  89. begin
  90. P := FControl.ScreenToClient(Mouse.CursorPos);
  91. if (P.X >= 0) and (P.X < FControl.Width) and
  92. (P.Y >= 0) and (P.Y < FControl.Height) then
  93. begin
  94. DragOver(P);
  95. end;
  96. end;
  97. procedure TCustomControlScrollOnDragOver.StartDrag;
  98. begin
  99. GetSystemTimeAsFileTime(FDragOverTime);
  100. GetSystemTimeAsFileTime(FLastVScrollTime);
  101. FVScrollCount := 0;
  102. if Assigned(FDragOverTimer) then
  103. FDragOverTimer.Enabled := True;
  104. end;
  105. procedure TCustomControlScrollOnDragOver.EndDrag;
  106. begin
  107. if Assigned(FDragOverTimer) then
  108. FDragOverTimer.Enabled := False;
  109. end;
  110. type
  111. TPublicControl = class(TControl);
  112. procedure TCustomControlScrollOnDragOver.BeforeUpdate(ObjectToValidate: TObject);
  113. var
  114. DragImages: TDragImageList;
  115. begin
  116. if Assigned(FOnBeforeUpdate) then
  117. FOnBeforeUpdate(ObjectToValidate);
  118. DragImages := TPublicControl(FControl).GetDragImages;
  119. if Assigned(DragImages) then
  120. DragImages.HideDragImage;
  121. end;
  122. procedure TCustomControlScrollOnDragOver.AfterUpdate;
  123. var
  124. DragImages: TDragImageList;
  125. begin
  126. if Assigned(FOnAfterUpdate) then
  127. FOnAfterUpdate;
  128. DragImages := TPublicControl(FControl).GetDragImages;
  129. if Assigned(DragImages) then
  130. DragImages.ShowDragImage;
  131. end;
  132. procedure TTreeViewScrollOnDragOver.StartDrag;
  133. var
  134. KeyBoardState : TKeyBoardState;
  135. begin
  136. inherited;
  137. FLastDragNode := nil;
  138. if (GetKeyState(VK_SPACE) <> 0) and GetKeyboardState(KeyBoardState) then
  139. begin
  140. KeyBoardState[VK_SPACE] := 0;
  141. SetKeyBoardState(KeyBoardState);
  142. end;
  143. GetSystemTimeAsFileTime(FLastHScrollTime);
  144. end;
  145. { TTreeViewScrollOnDragOver }
  146. procedure TTreeViewScrollOnDragOver.DragOver(Point: TPoint);
  147. var
  148. TreeView: TCustomTreeView;
  149. NbPixels: Integer;
  150. KnowTime: TFileTime;
  151. Node: TTreeNode;
  152. TempTopItem: TTreeNode;
  153. ScrollInfo: TScrollInfo;
  154. KeyBoardState : TKeyBoardState;
  155. begin
  156. TreeView := (FControl as TCustomTreeView);
  157. Node := TreeView.GetNodeAt(Point.X, Point.Y);
  158. if Assigned(Node) then
  159. begin
  160. GetSystemTimeAsFileTime(KnowTime);
  161. if GetKeyState(VK_SPACE) = 0 then
  162. begin
  163. {Expand node after 2.5 seconds: }
  164. if not Assigned(FLastDragNode) or (FLastDragNode <> Node) then
  165. begin
  166. {not previous droptarget: start timer}
  167. GetSystemTimeAsFileTime(FDragOverTime);
  168. FLastDragNode := Node
  169. end
  170. else
  171. begin
  172. if ((Int64(KnowTime) - Int64(FDragOverTime)) > DDExpandDelay) then
  173. begin
  174. TempTopItem := TreeView.TopItem;
  175. BeforeUpdate(nil);
  176. Node.Expand(False);
  177. TreeView.TopItem := TempTopItem;
  178. TreeView.Update;
  179. AfterUpdate;
  180. FDragOverTime := KnowTime;
  181. end;
  182. end;
  183. end
  184. else
  185. begin
  186. {restart timer}
  187. GetSystemTimeAsFileTime(FDragOverTime);
  188. if GetKeyboardState(KeyBoardState) then
  189. begin
  190. KeyBoardState[VK_Space] := 0;
  191. SetKeyBoardState(KeyBoardState);
  192. end;
  193. TempTopItem := TreeView.TopItem;
  194. BeforeUpdate(Node);
  195. if Node.Expanded then
  196. begin
  197. if not TreeView.Selected.HasAsParent(Node) then
  198. Node.Collapse(False);
  199. end
  200. else Node.Expand(False);
  201. TreeView.TopItem := TempTopItem;
  202. TreeView.Update;
  203. AfterUpdate;
  204. end;
  205. NbPixels := Abs(TTreeView(FControl).Font.Height);
  206. {Vertical treescrolling:}
  207. if ((Int64(KnowTime) - Int64(FLastVScrollTime)) > DDVScrollDelay) or
  208. ((FVScrollCount > 3) and
  209. ((Int64(KnowTime) - Int64(FLastVScrollTime)) > (DDVScrollDelay Div 4))) then
  210. begin
  211. {Scroll tree up, if droptarget is topitem:}
  212. if Node = TreeView.TopItem then
  213. begin
  214. BeforeUpdate(nil);
  215. TreeView.Perform(WM_VSCROLL, SB_LINEUP, 0);
  216. AfterUpdate;
  217. GetSystemTimeAsFileTime(FLastVScrollTime);
  218. Inc(FVScrollCount);
  219. end
  220. else
  221. {Scroll tree down, if next visible item of droptarget is not visible:}
  222. begin
  223. if Point.Y + 3 * nbPixels > TreeView.Height then
  224. begin
  225. BeforeUpdate(nil);
  226. TreeView.Perform(WM_VSCROLL, SB_LINEDOWN, 0);
  227. AfterUpdate;
  228. GetSystemTimeAsFileTime(FLastVScrollTime);
  229. Inc(FVScrollCount);
  230. end
  231. else
  232. begin
  233. FVScrollCount := 0;
  234. end;
  235. end;
  236. end; {VScrollDelay}
  237. {Horizontal treescrolling:}
  238. {Scroll tree Left}
  239. if ((Int64(KnowTime) - Int64(FLastHScrollTime)) > DDHScrollDelay) then
  240. begin
  241. GetSystemTimeAsFileTime(FLastHScrollTime);
  242. ScrollInfo.cbSize := SizeOf(ScrollInfo);
  243. ScrollInfo.FMask := SIF_ALL;
  244. GetScrollInfo(TreeView.Handle, SB_HORZ, ScrollInfo);
  245. if ScrollInfo.nMin <> ScrollInfo.nMax then
  246. begin
  247. if Point.X < 50 then
  248. begin
  249. if Node.DisplayRect(True).Right + 50 < TreeView.Width then
  250. begin
  251. BeforeUpdate(nil);
  252. TreeView.Perform(WM_HSCROLL, SB_LINELEFT, 0);
  253. AfterUpdate;
  254. end;
  255. end
  256. else
  257. if Point.X > (TreeView.Width - 50) then
  258. begin
  259. if Node.DisplayRect(True).Left > 50 then
  260. begin
  261. BeforeUpdate(nil);
  262. TreeView.Perform(WM_HSCROLL, SB_LINERIGHT, 0);
  263. AfterUpdate;
  264. end;
  265. end;
  266. end;
  267. end;
  268. end;
  269. end;
  270. { TListViewScrollOnDragOver }
  271. procedure TListViewScrollOnDragOver.DragOver(Point: TPoint);
  272. var
  273. ListView: TCustomListView;
  274. KnowTime: TFileTime;
  275. NbPixels: Integer;
  276. WParam: LongInt;
  277. begin
  278. ListView := (FControl as TCustomListView);
  279. GetSystemTimeAsFileTime(KnowTime);
  280. NbPixels := Abs(TListView(ListView).Font.Height);
  281. {Vertical scrolling, if viewstyle = vsReport:}
  282. if (TListView(ListView).ViewStyle = vsReport) and Assigned(ListView.TopItem) and
  283. (((Int64(KnowTime) - Int64(FLastVScrollTime)) > DDVScrollDelay) or
  284. ((FVScrollCount > DDMaxSlowCount) and
  285. ((Int64(KnowTime) - Int64(FLastVScrollTime)) > (DDVScrollDelay div 4)))) then
  286. begin
  287. if (Point.Y - 3 * nbPixels <= 0) and (ListView.TopItem.Index > 0) then WParam := SB_LINEUP
  288. else
  289. if (Point.Y + 3 * nbPixels > ListView.Height) then WParam := SB_LINEDOWN
  290. else WParam := -1;
  291. if WParam >= 0 then
  292. begin
  293. BeforeUpdate(nil);
  294. ListView.Perform(WM_VSCROLL, WParam, 0);
  295. if FVScrollCount > DDMaxSlowCount then
  296. ListView.Perform(WM_VSCROLL, WParam, 0);
  297. if FVScrollCount > DDMaxSlowCount * 3 then
  298. ListView.Perform(WM_VSCROLL, WParam, 0);
  299. ListView.Update;
  300. AfterUpdate;
  301. GetSystemTimeAsFileTime(FLastVScrollTime);
  302. Inc(FVScrollCount);
  303. end
  304. else FVScrollCount := 0;
  305. end;
  306. end;
  307. { TListBoxScrollOnDragOver }
  308. procedure TListBoxScrollOnDragOver.DragOver(Point: TPoint);
  309. var
  310. ListBox: TListBox;
  311. KnowTime: TFileTime;
  312. NbPixels: Integer;
  313. WParam: LongInt;
  314. begin
  315. ListBox := (FControl as TListBox);
  316. GetSystemTimeAsFileTime(KnowTime);
  317. NbPixels := Abs(ListBox.Font.Height);
  318. {Vertical scrolling, if viewstyle = vsReport:}
  319. if (ListBox.Items.Count > 0) and
  320. (((Int64(KnowTime) - Int64(FLastVScrollTime)) > DDVScrollDelay) or
  321. ((FVScrollCount > DDMaxSlowCount) and
  322. ((Int64(KnowTime) - Int64(FLastVScrollTime)) > (DDVScrollDelay div 4)))) then
  323. begin
  324. if (Point.Y - 3 * nbPixels <= 0) and (ListBox.TopIndex > 0) then WParam := SB_LINEUP
  325. else
  326. if (Point.Y + 3 * nbPixels > ListBox.Height) then WParam := SB_LINEDOWN
  327. else WParam := -1;
  328. if WParam >= 0 then
  329. begin
  330. BeforeUpdate(nil);
  331. ListBox.Perform(WM_VSCROLL, WParam, 0);
  332. if FVScrollCount > DDMaxSlowCount then
  333. ListBox.Perform(WM_VSCROLL, WParam, 0);
  334. if FVScrollCount > DDMaxSlowCount * 3 then
  335. ListBox.Perform(WM_VSCROLL, WParam, 0);
  336. ListBox.Update;
  337. AfterUpdate;
  338. GetSystemTimeAsFileTime(FLastVScrollTime);
  339. Inc(FVScrollCount);
  340. end
  341. else FVScrollCount := 0;
  342. end;
  343. end;
  344. end.