CnMDIBackGround.pas 17 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675676677678679680681682683684
  1. {******************************************************************************}
  2. { CnPack For Delphi/C++Builder }
  3. { 中国人自己的开放源码第三方开发包 }
  4. { (C)Copyright 2001-2018 CnPack 开发组 }
  5. { ------------------------------------ }
  6. { }
  7. { 本开发包是开源的自由软件,您可以遵照 CnPack 的发布协议来修 }
  8. { 改和重新发布这一程序。 }
  9. { }
  10. { 发布这一开发包的目的是希望它有用,但没有任何担保。甚至没有 }
  11. { 适合特定目的而隐含的担保。更详细的情况请参阅 CnPack 发布协议。 }
  12. { }
  13. { 您应该已经和开发包一起收到一份 CnPack 发布协议的副本。如果 }
  14. { 还没有,可访问我们的网站: }
  15. { }
  16. { 网站地址:http://www.cnpack.org }
  17. { 电子邮件:master@cnpack.org }
  18. { }
  19. {******************************************************************************}
  20. unit CnMDIBackGround;
  21. {* |<PRE>
  22. ================================================================================
  23. * 软件名称:不可视工具组件包
  24. * 单元名称:MDI 主窗体画背景单元
  25. * 单元作者:Shenloqi
  26. * 备 注:
  27. * 开发平台:PWin2000Pro + Delphi 5.01
  28. * 兼容测试:PWin9X/2000/XP + Delphi 5/6/7 + C++Builder 5/6
  29. * 本 地 化:该单元中的字符串支持本地化处理方式
  30. * 单元标识:$Id$
  31. * 修改记录:2004.06.08
  32. * 创建单元
  33. ================================================================================
  34. |</PRE>}
  35. interface
  36. {$I CnPack.inc}
  37. uses
  38. SysUtils, Windows, Messages, Classes, Controls, Forms, StdCtrls, ExtCtrls,
  39. Graphics, CnConsts, CnClasses, CnCompConsts;
  40. type
  41. TCnBMPDisplayStyle = (dsNormal, dsTiled, dsStretched, dsCentered, dsNone);
  42. TPaintImageEvent = procedure(Sender: TObject; ACanvas: TCanvas) of object;
  43. TCnMDIBackGround = class(TCnComponent)
  44. private
  45. { Private declarations }
  46. OldWndProc: TFarProc;
  47. NewWndProc: Pointer;
  48. OldMDIWndProc: TFarProc;
  49. NewMDIWndProc: Pointer;
  50. FBitmap: TBitmap;
  51. FDisplayStyle: TCnBMPDisplayStyle;
  52. FColor: TColor;
  53. FBuffer: TBitmap;
  54. FBorderLeft: Integer;
  55. FBorderRight: Integer;
  56. FBorderBottom: Integer;
  57. FBorderTop: Integer;
  58. FOnPaintImage: TPaintImageEvent;
  59. procedure SetBitmap(const Value: TBitmap);
  60. procedure SetDStyle(const Value: TCnBMPDisplayStyle);
  61. procedure SetMDIColor(const Value: TColor);
  62. protected
  63. { Protected declarations }
  64. procedure HookWndProc(var AMsg: TMessage);
  65. procedure HookWnd;
  66. procedure UnHookWnd;
  67. procedure HookMDIWndProc(var AMsg: TMessage);
  68. procedure HookMDIWin;
  69. procedure UnhookMDIWin;
  70. procedure PaintImage(const Msg, wParam, lParam: DWORD);
  71. procedure DoPaintImage(ACanvas: TCanvas);
  72. procedure GetComponentInfo(var AName, Author, Email, Comment: string); override;
  73. public
  74. { Public declarations }
  75. constructor Create(AOwner: TComponent); override;
  76. destructor Destroy; override;
  77. procedure DrawImage(ACanvas: TCanvas; AImage: TImage);
  78. procedure DrawLabel(ACanvas: TCanvas; ALabel: TLabel);
  79. published
  80. { Published declarations }
  81. property Bitmap: TBitmap read FBitmap write SetBitmap;
  82. property BorderBottom: Integer read FBorderBottom write FBorderBottom;
  83. property BorderLeft: Integer read FBorderLeft write FBorderLeft;
  84. property BorderRight: Integer read FBorderRight write FBorderRight;
  85. property BorderTop: Integer read FBorderTop write FBorderTop;
  86. property Color: TColor read FColor write SetMDIColor default clappWorkspace;
  87. property DisplayStyle: TCnBMPDisplayStyle read FDisplayStyle write SetDStyle default dsNone;
  88. property OnPaintImage: TPaintImageEvent read FOnPaintImage write FOnPaintImage;
  89. end;
  90. TCnWinControlHookList = class(TObject)
  91. private
  92. FWinControl: TWinControl;
  93. FHooks: TList;
  94. public
  95. constructor Create(aWinControl: TWinControl);
  96. destructor Destroy; override;
  97. property WinControl: TWinControl read FWinControl;
  98. procedure AddHook(oldHook: TFarProc);
  99. function GetNextHook: TFarProc;
  100. function Count: integer;
  101. end;
  102. procedure PushOldProc(aWinControl: TWinControl; OldHook: TFarProc);
  103. function PopOldProc(aWinControl: TWinControl): TFarProc;
  104. implementation
  105. uses
  106. Math;
  107. var
  108. FormList: TList;
  109. procedure PushOldProc(aWinControl: TWinControl; OldHook: TFarProc);
  110. var
  111. iloop: Integer;
  112. wHook: TCnWinControlHookList;
  113. bfound: Boolean;
  114. begin
  115. bfound := False;
  116. wHook := nil;
  117. for iloop := 0 to FormList.Count - 1 do
  118. begin
  119. wHook := TCnWinControlHookList(FormList[iloop]);
  120. bfound := wHook.WinControl = aWinControl;
  121. if bfound then
  122. Break;
  123. end;
  124. if bfound then
  125. wHook.AddHook(OldHook)
  126. else
  127. begin
  128. if Assigned(aWinControl) then
  129. begin
  130. wHook := TCnWinControlHookList.Create(aWinControl);
  131. FormList.Add(wHook);
  132. wHook.AddHook(oldhook);
  133. end
  134. end
  135. end;
  136. function PopOldProc(aWinControl: TWinControl): TFarProc;
  137. var
  138. iloop: Integer;
  139. wHook: TCnWinControlHookList;
  140. bfound: Boolean;
  141. begin
  142. bfound := False;
  143. wHook := nil;
  144. for iloop := 0 to FormList.Count - 1 do
  145. begin
  146. wHook := TCnWinControlHookList(FormList[iloop]);
  147. bfound := wHook.WinControl = aWinControl;
  148. if bfound then
  149. Break;
  150. end;
  151. if bfound then
  152. begin
  153. Result := wHook.GetNextHook;
  154. if wHook.Count = 0 then
  155. begin
  156. FormList.Delete(iloop);
  157. wHook.Free;
  158. end
  159. end
  160. else
  161. Result := nil;
  162. end;
  163. function _Width(const Rect: TRect): Integer;
  164. begin
  165. Result := Rect.Right - Rect.Left;
  166. end;
  167. function _Height(const Rect: TRect): Integer;
  168. begin
  169. Result := Rect.Bottom - Rect.Top;
  170. end;
  171. { TCnMDIBackGround }
  172. constructor TCnMDIBackGround.Create(AOwner: TComponent);
  173. begin
  174. inherited;
  175. if not ((AOwner is TForm) and (TForm(AOwner).FormStyle = fsMDIForm)) then
  176. raise Exception.Create('TCnMDIBackGround''s Owner MUST be MDIForm.');
  177. NewWndProc := nil;
  178. OldWndProc := nil;
  179. OldMDIWndProc := nil;
  180. NewMDIWndProc := nil;
  181. FBitmap := TBitmap.Create;
  182. FBuffer := TBitmap.Create;
  183. FColor := clAppWorkSpace;
  184. FDisplayStyle := dsNone;
  185. HookWnd;
  186. end;
  187. destructor TCnMDIBackGround.Destroy;
  188. begin
  189. UnHookWnd;
  190. FBitmap.Free;
  191. FBuffer.Free;
  192. inherited;
  193. end;
  194. procedure TCnMDIBackGround.DoPaintImage(ACanvas: TCanvas);
  195. begin
  196. if Assigned(FOnPaintImage) then
  197. FOnPaintImage(Self, ACanvas)
  198. end;
  199. procedure TCnMDIBackGround.DrawImage(ACanvas: TCanvas; AImage: TImage);
  200. var
  201. DescRect, Rect: TRect;
  202. Buffer: TBitmap;
  203. cx, cy: Integer;
  204. begin
  205. if not Assigned(AImage) then
  206. Exit;
  207. if AImage.Picture.Graphic.Empty then
  208. Exit;
  209. CopyRect(Rect, AImage.ClientRect);
  210. OffsetRect(Rect, AImage.Left, AImage.Top);
  211. //忽略 Proportional 和 IncrementalDisplay
  212. if AImage.AutoSize then
  213. begin
  214. ACanvas.Draw(Rect.Left, Rect.Top, AImage.Picture.Graphic);
  215. Exit
  216. end
  217. else if AImage.Stretch then
  218. begin
  219. ACanvas.StretchDraw(Rect, AImage.Picture.Graphic);
  220. Exit
  221. end;
  222. Buffer := TBitmap.Create;
  223. try
  224. Buffer.Height := AImage.Picture.Height;
  225. Buffer.Width := AImage.Picture.Width;
  226. Buffer.Canvas.Draw(0, 0, AImage.Picture.Graphic);
  227. if AImage.Center then
  228. begin
  229. cx := (AImage.Width - Buffer.Width) div 2;
  230. cy := (AImage.Height - Buffer.Height) div 2;
  231. Rect := Classes.Rect(Rect.Left + Max(cx, 0),
  232. Rect.Top + Max(cy, 0),
  233. Rect.Right - Max(cx, 0),
  234. Rect.Bottom - Max(cy, 0));
  235. DescRect := Rect;
  236. OffsetRect(DescRect, Max(-cx, 0) - DescRect.Left, Max(-cy, 0) - DescRect.Top)
  237. end
  238. else
  239. begin
  240. cx := Min(AImage.Width, Buffer.Width);
  241. cy := Min(AImage.Height, Buffer.Height);
  242. Rect := Classes.Rect(Rect.Left,
  243. Rect.Top,
  244. Rect.Left + cx,
  245. Rect.Top + cy);
  246. DescRect := Rect;
  247. OffsetRect(DescRect, - DescRect.Left, - DescRect.Top)
  248. end;
  249. ACanvas.CopyRect(Rect, Buffer.Canvas, DescRect)
  250. finally
  251. Buffer.Free;
  252. end
  253. end;
  254. procedure TCnMDIBackGround.DrawLabel(ACanvas: TCanvas; ALabel: TLabel);
  255. const
  256. Alignments: array[TAlignment] of Word = (DT_LEFT, DT_RIGHT, DT_CENTER);
  257. WordWraps: array[Boolean] of Word = (0, DT_WORDBREAK);
  258. var
  259. CalcRect, Rect: TRect;
  260. begin
  261. if not Assigned(ALabel) then
  262. Exit;
  263. CopyRect(Rect, ALabel.ClientRect);
  264. OffsetRect(Rect, ALabel.Left, ALabel.Top);
  265. with ACanvas do
  266. begin
  267. if not ALabel.Transparent then
  268. begin
  269. Brush.Color := ALabel.Color;
  270. Brush.Style := bsSolid;
  271. FillRect(Rect);
  272. end;
  273. Brush.Style := bsClear;
  274. Font := ALabel.Font;
  275. if ALabel.Layout <> tlTop then
  276. begin
  277. CalcRect := Rect;
  278. DrawText(Handle, PChar(ALabel.Caption), Length(ALabel.Caption), CalcRect,
  279. ALabel.DrawTextBiDiModeFlags(DT_EXPANDTABS or WordWraps[ALabel.WordWrap]
  280. or Alignments[ALabel.Alignment] or DT_NOPREFIX or DT_CALCRECT));
  281. if ALabel.Layout = tlBottom then
  282. OffsetRect(Rect, 0, _Height(Rect) - _Height(CalcRect))
  283. else
  284. OffsetRect(Rect, 0, (_Height(Rect) - _Height(CalcRect)) div 2);
  285. end;
  286. DrawText(Handle, PChar(ALabel.Caption), Length(ALabel.Caption), Rect,
  287. ALabel.DrawTextBiDiModeFlags(DT_EXPANDTABS or WordWraps[ALabel.WordWrap]
  288. or Alignments[ALabel.Alignment] or DT_NOPREFIX));
  289. end
  290. end;
  291. procedure TCnMDIBackGround.GetComponentInfo(var AName, Author, Email,
  292. Comment: string);
  293. begin
  294. AName := SCnMDIBackGroundName;
  295. Author := SCnPack_Shenloqi;
  296. Email := SCnPack_ShenloqiEmail;
  297. Comment := SCnMDIBackGroundComment;
  298. end;
  299. procedure TCnMDIBackGround.HookMDIWin;
  300. begin
  301. if csDesigning in ComponentState then
  302. Exit;
  303. if not Assigned(NewMDIWndProc) then
  304. begin
  305. OldMDIWndProc := TFarProc(GetWindowLong(TForm(Owner).ClientHandle, GWL_WNDPROC));
  306. NewMDIWndProc := MakeObjectInstance(HookMDIWndProc);
  307. SetWindowLong(TForm(Owner).ClientHandle, GWL_WNDPROC, LongInt(NewMDIWndProc));
  308. end
  309. end;
  310. procedure TCnMDIBackGround.HookMDIWndProc(var AMsg: TMessage);
  311. begin
  312. with AMsg do
  313. begin
  314. Result := CallWindowProc(OldMDIWndProc, TForm(Owner).ClientHandle, Msg, wParam, lParam);
  315. if Msg in [WM_PAINT{, WM_NCPAINT, WM_ERASEBKGND}] then
  316. PaintImage(Msg, wParam, lParam);
  317. end
  318. end;
  319. procedure TCnMDIBackGround.HookWnd;
  320. begin
  321. if csDesigning in ComponentState then
  322. Exit;
  323. if TForm(Owner).FormStyle <> fsMDIForm then
  324. Exit;
  325. if not Assigned(NewWndProc) then
  326. begin
  327. OldWndProc := TFarProc(GetWindowLong(TForm(Owner).Handle, GWL_WNDPROC));
  328. NewWndProc := MakeObjectInstance(HookWndProc);
  329. SetWindowLong(TForm(Owner).Handle, GWL_WNDPROC, LongInt(NewWndProc));
  330. PushOldProc(TForm(Owner), OldWndProc);
  331. HookMDIWin
  332. end
  333. end;
  334. procedure TCnMDIBackGround.HookWndProc(var AMsg: TMessage);
  335. begin
  336. case AMsg.Msg of
  337. WM_DESTROY:
  338. begin
  339. AMsg.Result := CallWindowProc(OldWndProc, TForm(Owner).Handle, AMsg.Msg, AMsg.wParam, AMsg.lParam);
  340. UnHookWnd;
  341. Exit
  342. end;
  343. end;
  344. AMsg.Result := CallWindowProc(OldWndProc, TForm(Owner).Handle, AMsg.Msg, AMsg.wParam, AMsg.lParam);
  345. case aMsg.Msg of
  346. //WM_ERASEBKGND,
  347. //WM_NCPAINT,
  348. WM_PAINT: PaintImage(AMsg.Msg, AMsg.wParam, AMsg.lParam)
  349. end;
  350. end;
  351. procedure TCnMDIBackGround.PaintImage(const Msg, wParam, lParam: DWORD);
  352. var
  353. ACanvas: TCanvas;
  354. DC: HDC;
  355. cx, cy: Integer;
  356. wRect, DescRect: TRect;
  357. x, y: Integer;
  358. procedure _ClearBuffer;
  359. begin
  360. FBuffer.Canvas.FillRect(Rect(0, 0, FBuffer.Width, FBuffer.Height))
  361. end;
  362. procedure _BufferToDC;
  363. begin
  364. BitBlt(DC,
  365. 0,
  366. 0,
  367. _Width(wRect),
  368. _Height(wRect),
  369. FBuffer.Canvas.Handle,
  370. 0,
  371. 0,
  372. SRCCOPY);
  373. end;
  374. begin
  375. if csDesigning in ComponentState then
  376. Exit;
  377. if TForm(Owner).FormStyle <> fsMDIForm then
  378. Exit;
  379. GetWindowRect(TForm(Owner).ClientHandle, wRect);
  380. FBuffer.Height := _Height(wRect);
  381. FBuffer.Width := _Width(wRect);
  382. if FBitmap.Empty then
  383. begin
  384. DC := GetDC(TForm(Owner).ClientHandle);
  385. try
  386. ACanvas := FBuffer.Canvas;
  387. ACanvas.Brush.Color := FColor;
  388. _ClearBuffer;
  389. DoPaintImage(ACanvas);
  390. _BufferToDC;
  391. Exit
  392. finally
  393. ReleaseDC(TForm(Owner).ClientHandle, DC)
  394. end
  395. end;
  396. if (FBitmap.Width = 0) or (FBitmap.Height = 0) then
  397. Exit;
  398. DescRect.Left := FBorderLeft;
  399. DescRect.Top := FBorderTop;
  400. DescRect.Right := _Width(wRect) - FBorderRight;
  401. DescRect.Bottom := _Height(wRect) - FBorderBottom;
  402. DC := GetDC(TForm(Owner).ClientHandle);
  403. try
  404. ACanvas := FBuffer.Canvas;
  405. ACanvas.Brush.Color := FColor;
  406. case FDisplayStyle of
  407. dsNormal, dsTiled, dsStretched, dsCentered:
  408. begin
  409. case FDisplayStyle of
  410. dsNormal:
  411. begin
  412. _ClearBuffer;
  413. BitBlt(FBuffer.Canvas.Handle,
  414. DescRect.Left,
  415. DescRect.Top,
  416. Min(FBitmap.Width, _Width(DescRect)),
  417. Min(FBitmap.Height, _Height(DescRect)),
  418. FBitmap.Canvas.Handle,
  419. 0,
  420. 0,
  421. SRCCOPY);
  422. DoPaintImage(ACanvas);
  423. end;
  424. dsTiled:
  425. begin
  426. _ClearBuffer;
  427. cx := DescRect.Right;
  428. cy := DescRect.Bottom;
  429. y := DescRect.Top;
  430. while y < cy do
  431. begin
  432. x := DescRect.Left;
  433. while x < cx do
  434. begin
  435. BitBlt(FBuffer.Canvas.Handle,
  436. x,
  437. y,
  438. Min(DescRect.Right - x, FBitmap.Width),
  439. Min(DescRect.Bottom - y, FBitmap.Height),
  440. FBitmap.Canvas.Handle,
  441. 0,
  442. 0,
  443. SRCCOPY);
  444. Inc(x, FBitmap.Width)
  445. end;
  446. Inc(y, FBitmap.Height)
  447. end;
  448. DoPaintImage(ACanvas);
  449. end;
  450. dsStretched:
  451. begin
  452. _ClearBuffer;
  453. cx := (wRect.Right - wRect.Left - FBorderLeft - FBorderRight);
  454. cy := (wRect.Bottom - wRect.Top - FBorderTop - FBorderBottom);
  455. FBuffer.Canvas.StretchDraw(Rect(DescRect.Left,DescRect.Top,cx,cy), FBitmap); ///Edit By LXY
  456. DoPaintImage(ACanvas);
  457. end;
  458. dsCentered:
  459. begin
  460. _ClearBuffer;
  461. cx := (_Width(DescRect) - FBitmap.Width) div 2;
  462. cy := (_Height(DescRect) - FBitmap.Height) div 2;
  463. BitBlt(FBuffer.Canvas.Handle,
  464. Max(DescRect.Left, cx),
  465. Max(DescRect.Top, cy),
  466. Min(FBitmap.Width, _Width(DescRect)),
  467. Min(FBitmap.Height, _Height(DescRect)),
  468. FBitmap.Canvas.Handle,
  469. Max(0, -cx),
  470. Max(0, -cy),
  471. SRCCOPY);
  472. DoPaintImage(ACanvas);
  473. end
  474. end
  475. end;
  476. dsNone:
  477. begin
  478. _ClearBuffer;
  479. DoPaintImage(ACanvas);
  480. end;
  481. end; // end case
  482. _BufferToDC;
  483. finally
  484. ReleaseDC(TForm(Owner).ClientHandle, DC)
  485. end
  486. end;
  487. procedure TCnMDIBackGround.SetBitmap(const Value: TBitmap);
  488. begin
  489. FBitmap.Assign(Value);
  490. end;
  491. procedure TCnMDIBackGround.SetDStyle(const Value: TCnBMPDisplayStyle);
  492. begin
  493. if FDisplayStyle <> Value then
  494. begin
  495. FDisplayStyle := Value;
  496. TForm(Owner).Invalidate;
  497. end
  498. end;
  499. procedure TCnMDIBackGround.SetMDIColor(const Value: TColor);
  500. begin
  501. if FColor <> Value then
  502. begin
  503. FColor := Value;
  504. TForm(Owner).Invalidate;
  505. end
  506. end;
  507. procedure TCnMDIBackGround.UnhookMDIWin;
  508. begin
  509. if csDesigning in ComponentState then
  510. Exit;
  511. if Assigned(NewMDIWndProc) then
  512. begin
  513. SetWindowLong(TForm(Owner).ClientHandle, GWL_WNDPROC, LongInt(OldMDIWndProc));
  514. if Assigned(NewMDIWndProc) then
  515. FreeObjectInstance(NewMDIWndProc);
  516. NewMDIWndProc := nil;
  517. OldMDIWndProc := nil;
  518. end
  519. end;
  520. procedure TCnMDIBackGround.UnHookWnd;
  521. begin
  522. if csDesigning in ComponentState then
  523. Exit;
  524. if Assigned(NewWndProc) then
  525. begin
  526. SetWindowLong(TForm(Owner).Handle, GWL_WNDPROC, LongInt(PopOldProc(TForm(Owner))));
  527. if Assigned(NewWndProc) then
  528. FreeObjectInstance(NewWndProc);
  529. NewWndProc := nil;
  530. OldWndProc := nil;
  531. end;
  532. UnHookMDIWin;
  533. end;
  534. { TCnWinControlHookList }
  535. procedure TCnWinControlHookList.AddHook(oldHook: TFarProc);
  536. begin
  537. FHooks.add(oldHook)
  538. end;
  539. function TCnWinControlHookList.Count: integer;
  540. begin
  541. Result := FHooks.Count
  542. end;
  543. constructor TCnWinControlHookList.Create(aWinControl: TWinControl);
  544. begin
  545. FWinControl := aWinControl;
  546. FHooks := TList.Create
  547. end;
  548. destructor TCnWinControlHookList.Destroy;
  549. begin
  550. FHooks.Free;
  551. inherited;
  552. end;
  553. function TCnWinControlHookList.GetNextHook: TFarProc;
  554. begin
  555. Result := FHooks[FHooks.Count - 1];
  556. FHooks.Delete(FHooks.Count - 1);
  557. end;
  558. initialization
  559. FormList := TList.Create;
  560. finalization
  561. FormList.Free;
  562. end.