Rxhints.pas
上传用户:hylc_2004
上传日期:2014-01-23
资源大小:46800k
文件大小:15k
源码类别:

Delphi控件源码

开发平台:

Delphi

  1. {*******************************************************}
  2. {                                                       }
  3. {         Delphi VCL Extensions (RX)                    }
  4. {                                                       }
  5. {         Copyright (c) 1997 Master-Bank                }
  6. {                                                       }
  7. {*******************************************************}
  8. unit RxHints;
  9. {$I RX.INC}
  10. interface
  11. uses {$IFDEF WIN32} Windows, {$ELSE} WinTypes, WinProcs, {$ENDIF} Messages,
  12.   Graphics, Classes, Controls, Forms, Dialogs;
  13. type
  14.   THintStyle = (hsRectangle, hsRoundRect, hsEllipse);
  15.   THintPos = (hpTopRight, hpTopLeft, hpBottomRight, hpBottomLeft);
  16.   THintShadowSize = 0..15;
  17.   TRxHintWindow = class(THintWindow)
  18.   private
  19.     FSrcImage: TBitmap;
  20.     FImage: TBitmap;
  21.     FPos: THintPos;
  22.     FRect: TRect;
  23.     FTextRect: TRect;
  24.     FTileSize: TPoint;
  25.     FRoundFactor: Integer;
  26.     procedure WMEraseBkgnd(var Message: TMessage); message WM_ERASEBKGND;
  27. {$IFDEF RX_D3}
  28.     procedure WMNCPaint(var Message: TMessage); message WM_NCPAINT;
  29. {$ENDIF}
  30.     function CreateRegion(Shade: Boolean): HRgn;
  31.     procedure FillRegion(Rgn: HRgn; Shade: Boolean);
  32.   protected
  33.     procedure CreateParams(var Params: TCreateParams); override;
  34.     procedure Paint; override;
  35.   public
  36.     constructor Create(AOwner: TComponent); override;
  37.     destructor Destroy; override;
  38.     procedure ActivateHint(Rect: TRect; const AHint: string); override;
  39. {$IFDEF RX_D3}
  40.     procedure ActivateHintData(Rect: TRect; const AHint: string;
  41.       AData: Pointer); override;
  42. {$ENDIF}
  43.     function CalcHintRect(MaxWidth: Integer; const AHint: string;
  44.       AData: Pointer): TRect; {$IFDEF RX_D3} override; {$ENDIF}
  45.   end;
  46. procedure SetHintStyle(Style: THintStyle; ShadowSize: THintShadowSize;
  47.   Tail: Boolean; Alignment: TAlignment);
  48. procedure SetStandardHints;
  49. procedure RegisterHintWindow(AClass: THintWindowClass);
  50. function GetHintControl: TControl;
  51. implementation
  52. uses SysUtils, VclUtils, AppUtils, MaxMin;
  53. const
  54.   HintStyle: THintStyle = hsRectangle;
  55.   HintShadowSize: THintShadowSize = 0;
  56.   HintTail: Boolean = False;
  57.   HintAlignment: TAlignment = taLeftJustify;
  58. { Utility routines }
  59. procedure RegisterHintWindow(AClass: THintWindowClass);
  60. begin
  61.   HintWindowClass := AClass;
  62.   with Application do
  63.     if ShowHint then begin
  64.       ShowHint := False;
  65.       ShowHint := True;
  66.     end;
  67. end;
  68. procedure SetStandardHints;
  69. begin
  70.   RegisterHintWindow(THintWindow);
  71. end;
  72. procedure SetHintStyle(Style: THintStyle; ShadowSize: THintShadowSize;
  73.   Tail: Boolean; Alignment: TAlignment);
  74. begin
  75.   HintStyle := Style;
  76.   HintShadowSize := ShadowSize;
  77.   HintTail := Tail;
  78.   HintAlignment := Alignment;
  79.   RegisterHintWindow(TRxHintWindow);
  80. end;
  81. function GetHintControl: TControl;
  82. var
  83.   CursorPos: TPoint;
  84. begin
  85.   GetCursorPos(CursorPos);
  86.   Result := FindDragTarget(CursorPos, True);
  87.   while (Result <> nil) and not Result.ShowHint do
  88.     Result := Result.Parent;
  89.   if (Result <> nil) and (csDesigning in Result.ComponentState) then
  90.     Result := nil;
  91. end;
  92. procedure StandardHintFont(AFont: TFont);
  93. {$IFDEF WIN32}
  94. var
  95.   NonClientMetrics: TNonClientMetrics;
  96. {$ENDIF}
  97. begin
  98. {$IFDEF WIN32}
  99.   NonClientMetrics.cbSize := SizeOf(NonClientMetrics);
  100.   if SystemParametersInfo(SPI_GETNONCLIENTMETRICS, 0, @NonClientMetrics, 0) then
  101.     AFont.Handle := CreateFontIndirect(NonClientMetrics.lfStatusFont)
  102.   else begin
  103.     AFont.Name := 'MS Sans Serif';
  104.     AFont.Size := 8;
  105.   end;
  106.   AFont.Color := clInfoText;
  107. {$ELSE}
  108.   AFont.Name := 'MS Sans Serif';
  109.   AFont.Size := 8;
  110.   AFont.Color := clWindowText;
  111. {$ENDIF}
  112. end;
  113. {$IFDEF WIN32}
  114. {$IFNDEF RX_D3}
  115. function GetCursorHeightMargin: Integer;
  116. { Return number of scanlines between the scanline containing cursor hotspot
  117.   and the last scanline included in the cursor mask. }
  118. var
  119.   IconInfo: TIconInfo;
  120.   BitmapInfoSize: Integer;
  121.   BitmapBitsSize: Integer;
  122.   Bitmap: PBitmapInfoHeader;
  123.   Bits: Pointer;
  124.   BytesPerScanline, ImageSize: Integer;
  125.     function FindScanline(Source: Pointer; MaxLen: Cardinal;
  126.       Value: Cardinal): Cardinal; assembler;
  127.     asm
  128.             PUSH    ECX
  129.             MOV     ECX,EDX
  130.             MOV     EDX,EDI
  131.             MOV     EDI,EAX
  132.             POP     EAX
  133.             REPE    SCASB
  134.             MOV     EAX,ECX
  135.             MOV     EDI,EDX
  136.     end;
  137. begin
  138.   { Default value is entire icon height }
  139.   Result := GetSystemMetrics(SM_CYCURSOR);
  140.   if GetIconInfo(GetCursor, IconInfo) then
  141.   try
  142.     GetDIBSizes(IconInfo.hbmMask, BitmapInfoSize, BitmapBitsSize);
  143.     Bitmap := AllocMem(BitmapInfoSize + BitmapBitsSize);
  144.     try
  145.       Bits := Pointer(Longint(Bitmap) + BitmapInfoSize);
  146.       if GetDIB(IconInfo.hbmMask, 0, Bitmap^, Bits^) and
  147.         (Bitmap^.biBitCount = 1) then
  148.       begin
  149.         { Point Bits to the end of this bottom-up bitmap }
  150.         with Bitmap^ do
  151.         begin
  152.           BytesPerScanline := ((biWidth * biBitCount + 31) and not 31) div 8;
  153.           ImageSize := biWidth * BytesPerScanline;
  154.           Bits := Pointer(Integer(Bits) + BitmapBitsSize - ImageSize);
  155.           { Use the width to determine the height since another mask bitmap
  156.             may immediately follow }
  157.           Result := FindScanline(Bits, ImageSize, $FF);
  158.           { In case the and mask is blank, look for an empty scanline in the
  159.             xor mask. }
  160.           if (Result = 0) and (biHeight >= 2 * biWidth) then
  161.             Result := FindScanline(Pointer(Integer(Bits) - ImageSize),
  162.               ImageSize, $00);
  163.           Result := Result div BytesPerScanline;
  164.         end;
  165.         Dec(Result, IconInfo.yHotSpot);
  166.       end;
  167.     finally
  168.       FreeMem(Bitmap, BitmapInfoSize + BitmapBitsSize);
  169.     end;
  170.   finally
  171.     if IconInfo.hbmColor <> 0 then DeleteObject(IconInfo.hbmColor);
  172.     if IconInfo.hbmMask <> 0 then DeleteObject(IconInfo.hbmMask);
  173.   end;
  174. end;
  175. {$ENDIF}
  176. {$ENDIF}
  177. { TRxHintWindow }
  178. constructor TRxHintWindow.Create(AOwner: TComponent);
  179. begin
  180.   inherited Create(AOwner);
  181.   StandardHintFont(Canvas.Font);
  182.   FImage := TBitmap.Create;
  183.   FSrcImage := TBitmap.Create;
  184. end;
  185. destructor TRxHintWindow.Destroy;
  186. begin
  187.   FSrcImage.Free;
  188.   FImage.Free;
  189.   inherited Destroy;
  190. end;
  191. procedure TRxHintWindow.CreateParams(var Params: TCreateParams);
  192. begin
  193.   inherited CreateParams(Params);
  194.   Params.Style := Params.Style and not WS_BORDER;
  195. end;
  196. {$IFDEF RX_D3}
  197. procedure TRxHintWindow.WMNCPaint(var Message: TMessage);
  198. begin
  199. end;
  200. {$ENDIF}
  201. procedure TRxHintWindow.WMEraseBkgnd(var Message: TMessage);
  202. begin
  203.   Message.Result := 1;
  204. end;
  205. function TRxHintWindow.CreateRegion(Shade: Boolean): HRgn;
  206. var
  207.   R: TRect;
  208.   W, TileOffs: Integer;
  209.   Tail, Dest: HRgn;
  210.   P: TPoint;
  211.   function CreatePolyRgn(const Points: array of TPoint): HRgn;
  212.   type
  213.     PPoints = ^TPoints;
  214.     TPoints = array[0..0] of TPoint;
  215.   begin
  216.     Result := CreatePolygonRgn(PPoints(@Points)^, High(Points) + 1, WINDING);
  217.   end;
  218. begin
  219.   R := FRect;
  220.   Result := 0;
  221.   if Shade then OffsetRect(R, HintShadowSize, HintShadowSize);
  222.   case HintStyle of
  223.     hsRoundRect: Result := CreateRoundRectRgn(R.Left, R.Top, R.Right, R.Bottom,
  224.       FRoundFactor, FRoundFactor);
  225.     hsEllipse: Result := CreateEllipticRgnIndirect(R);
  226.     hsRectangle: Result := CreateRectRgnIndirect(R);
  227.   end;
  228.   if HintTail then begin
  229.     R := FTextRect;
  230.     GetCursorPos(P);
  231.     TileOffs := 0;
  232.     if FPos in [hpTopLeft, hpBottomLeft] then TileOffs := Width;
  233.     if Shade then begin
  234.       OffsetRect(R, HintShadowSize, HintShadowSize);
  235.       Inc(TileOffs, HintShadowSize);
  236.     end;
  237.     W := Min(Max(8, Min(WidthOf(R), HeightOf(R)) div 4), WidthOf(R) div 2);
  238.     case FPos of
  239.       hpTopRight:
  240.         Tail := CreatePolyRgn([Point(TileOffs, Height - HintShadowSize),
  241.           Point(R.Left + W div 4, R.Bottom), Point(R.Left + 2 * W, R.Bottom)]);
  242.       hpTopLeft:
  243.         Tail := CreatePolyRgn([Point(TileOffs, Height - HintShadowSize),
  244.           Point(R.Right - W div 4, R.Bottom), Point(R.Right - 2 * W, R.Bottom)]);
  245.       hpBottomRight:
  246.         Tail := CreatePolyRgn([Point(TileOffs, 0),
  247.           Point(R.Left + W div 4, R.Top), Point(R.Left + 2 * W, R.Top)]);
  248.       else {hpBottomLeft}
  249.         Tail := CreatePolyRgn([Point(TileOffs, 0),
  250.           Point(R.Right - W div 4, R.Top), Point(R.Right - 2 * W, R.Top)]);
  251.     end;
  252.     try
  253.       Dest := Result;
  254.       Result := CreateRectRgnIndirect(R);
  255.       try
  256.         CombineRgn(Result, Dest, Tail, RGN_OR);
  257.       finally
  258.         if Dest <> 0 then DeleteObject(Dest);
  259.       end;
  260.     finally
  261.       DeleteObject(Tail);
  262.     end;
  263.   end;
  264. end;
  265. procedure TRxHintWindow.FillRegion(Rgn: HRgn; Shade: Boolean);
  266. begin
  267.   if Shade then begin
  268.     FImage.Canvas.Brush.Bitmap :=
  269. {$IFDEF RX_D4}
  270.       AllocPatternBitmap(clBtnFace, clWindowText);
  271. {$ELSE}
  272.       CreateTwoColorsBrushPattern(clBtnFace, clWindowText);
  273. {$ENDIF}
  274.     FImage.Canvas.Pen.Style := psClear;
  275.   end
  276.   else begin
  277.     FImage.Canvas.Pen.Style := psSolid;
  278.     FImage.Canvas.Brush.Color := Color;
  279.   end;
  280.   try
  281.     PaintRgn(FImage.Canvas.Handle, Rgn);
  282.     if not Shade then begin
  283.       FImage.Canvas.Brush.Color := Font.Color;
  284. {$IFDEF WIN32}
  285.       if (HintStyle = hsRectangle) and not HintTail then begin
  286.         DrawEdge(FImage.Canvas.Handle, FRect, BDR_RAISEDOUTER, BF_RECT);
  287.       end
  288.       else
  289. {$ENDIF}
  290.         FrameRgn(FImage.Canvas.Handle, Rgn, FImage.Canvas.Brush.Handle, 1, 1);
  291.     end;
  292.   finally
  293.     if Shade then begin
  294. {$IFDEF RX_D4}
  295.       FImage.Canvas.Brush.Bitmap := nil;
  296. {$ELSE}
  297.       FImage.Canvas.Brush.Bitmap.Free;
  298. {$ENDIF}
  299.       FImage.Canvas.Pen.Style := psSolid;
  300.     end;
  301.     FImage.Canvas.Brush.Color := Color;
  302.   end;
  303. end;
  304. procedure TRxHintWindow.Paint;
  305. var
  306.   R: TRect;
  307.   FShadeRgn, FRgn: HRgn;
  308.   procedure PaintText(R: TRect);
  309.   const
  310.     Flag: array[TAlignment] of Longint = (DT_LEFT, DT_RIGHT, DT_CENTER);
  311. {$IFNDEF WIN32}
  312.   var
  313.     ACaption: array[0..255] of Char;
  314. {$ENDIF}
  315.   begin
  316. {$IFDEF WIN32}
  317.     DrawText(FImage.Canvas.Handle, PChar(Caption),
  318. {$ELSE}
  319.     DrawText(FImage.Canvas.Handle, StrPCopy(ACaption, Caption),
  320. {$ENDIF}
  321.       -1, R, DT_NOPREFIX or DT_WORDBREAK or Flag[HintAlignment]
  322.       {$IFDEF RX_D4} or DrawTextBiDiModeFlagsReadingOnly {$ENDIF});
  323.   end;
  324. begin
  325.   R := ClientRect;
  326.   FImage.Handle := CreateCompatibleBitmap(Canvas.Handle,
  327.     WidthOf(ClientRect), HeightOf(ClientRect));
  328.   FImage.Canvas.Font := Self.Canvas.Font;
  329.   if (HintStyle <> hsRectangle) or (HintShadowSize > 0) or HintTail then
  330.     FImage.Canvas.Draw(0, 0, FSrcImage);
  331.   FRgn := CreateRegion(False);
  332.   FShadeRgn := CreateRegion(True);
  333.   try
  334.     FillRegion(FShadeRgn, True);
  335.     FillRegion(FRgn, False);
  336.   finally
  337.     DeleteObject(FShadeRgn);
  338.     DeleteObject(FRgn);
  339.   end;
  340.   R := FTextRect;
  341.   if HintAlignment = taLeftJustify then Inc(R.Left, 2);
  342.   PaintText(R);
  343.   Canvas.Draw(0, 0, FImage);
  344. end;
  345. procedure TRxHintWindow.ActivateHint(Rect: TRect; const AHint: string);
  346. var
  347.   R: TRect;
  348.   ScreenDC: HDC;
  349.   P: TPoint;
  350. begin
  351.   Caption := AHint;
  352.   GetCursorPos(P);
  353.   FPos := hpBottomRight;
  354.   R := CalcHintRect(Screen.Width, AHint, nil);
  355. {$IFDEF RX_D3}
  356.   OffsetRect(R, Rect.Left - R.Left, Rect.Top - R.Top);
  357. {$ELSE}
  358.  {$IFDEF WIN32}
  359.   OffsetRect(R, P.X, P.Y + GetCursorHeightMargin);
  360.  {$ELSE}
  361.   OffsetRect(R, P.X, Rect.Top - R.Top);
  362.  {$ENDIF WIN32}
  363. {$ENDIF}
  364.   Rect := R;
  365.   BoundsRect := Rect;
  366.   if HintTail then begin
  367.     Rect.Top := P.Y - Height - 3;
  368.     if Rect.Top < 0 then Rect.Top := BoundsRect.Top
  369.     else Rect.Bottom := Rect.Top + HeightOf(BoundsRect);
  370.     Rect.Left := P.X + 1;
  371.     if Rect.Left < 0 then Rect.Left := BoundsRect.Left
  372.     else Rect.Right := Rect.Left + WidthOf(BoundsRect);
  373.   end;
  374.   if Rect.Top + Height > Screen.Height then begin
  375.     Rect.Top := Screen.Height - Height;
  376.     if Rect.Top <= P.Y then Rect.Top := P.Y - Height - 3;
  377.   end;
  378.   if Rect.Left + Width > Screen.Width then begin
  379.     Rect.Left := Screen.Width - Width;
  380.     if Rect.Left <= P.X then Rect.Left := P.X - Width -3;
  381.   end;
  382.   if Rect.Left < 0 then begin
  383.     Rect.Left := 0;
  384.     if Rect.Left + Width >= P.X then Rect.Left := P.X - Width - 1;
  385.   end;
  386.   if Rect.Top < 0 then begin
  387.     Rect.Top := 0;
  388.     if Rect.Top + Height >= P.Y then Rect.Top := P.Y - Height - 1;
  389.   end;
  390.   if (HintStyle <> hsRectangle) or (HintShadowSize > 0) or HintTail then
  391.   begin
  392.     FPos := hpBottomRight;
  393.     if (Rect.Top + Height < P.Y) then FPos := hpTopRight;
  394.     if (Rect.Left + Width < P.X) then begin
  395.       if FPos = hpBottomRight then FPos := hpBottomLeft
  396.       else FPos := hpTopLeft;
  397.     end;
  398.     if HintTail then begin
  399.       if (FPos in [hpBottomRight, hpBottomLeft]) then begin
  400.         OffsetRect(FRect, 0, FTileSize.Y);
  401.         OffsetRect(FTextRect, 0, FTileSize.Y);
  402.       end;
  403.       if (FPos in [hpBottomRight, hpTopRight]) then begin
  404.         OffsetRect(FRect, FTileSize.X, 0);
  405.         OffsetRect(FTextRect, FTileSize.X, 0);
  406.       end;
  407.     end;
  408.     if HandleAllocated then begin
  409.       SetWindowPos(Handle, HWND_BOTTOM, 0, 0, 0, 0, SWP_HIDEWINDOW or
  410.         SWP_NOACTIVATE or SWP_NOSIZE or SWP_NOMOVE);
  411.       if Screen.ActiveForm <> nil then UpdateWindow(Screen.ActiveForm.Handle);
  412.     end;
  413.     ScreenDC := GetDC(0);
  414.     try
  415.       with FSrcImage do begin
  416.         Width := WidthOf(BoundsRect);
  417.         Height := HeightOf(BoundsRect);
  418.         BitBlt(Canvas.Handle, 0, 0, Width, Height, ScreenDC, Rect.Left,
  419.           Rect.Top, SRCCOPY);
  420.       end;
  421.     finally
  422.       ReleaseDC(0, ScreenDC);
  423.     end;
  424.   end;
  425.   SetWindowPos(Handle, HWND_TOPMOST, Rect.Left, Rect.Top, 0,
  426.     0, SWP_SHOWWINDOW or SWP_NOACTIVATE or SWP_NOSIZE);
  427. end;
  428. function TRxHintWindow.CalcHintRect(MaxWidth: Integer; const AHint: string;
  429.   AData: Pointer): TRect;
  430. const
  431.   Flag: array[TAlignment] of Longint = (DT_LEFT, DT_RIGHT, DT_CENTER);
  432. var
  433.   A: Integer;
  434.   X, Y, Factor: Double;
  435. {$IFNDEF WIN32}
  436.   ACaption: array[0..255] of Char;
  437. {$ENDIF}
  438. begin
  439.   Result := Rect(0, 0, MaxWidth, 0);
  440.   DrawText(Canvas.Handle,
  441. {$IFDEF WIN32}
  442.     PChar(AHint),
  443. {$ELSE}
  444.     StrPCopy(ACaption, AHint),
  445. {$ENDIF}
  446.     -1, Result, DT_CALCRECT or DT_WORDBREAK or DT_NOPREFIX or Flag[HintAlignment]
  447.     {$IFDEF RX_D4} or DrawTextBiDiModeFlagsReadingOnly {$ENDIF});
  448.   Inc(Result.Right, 8);
  449.   Inc(Result.Bottom, 4);
  450.   FRect := Result;
  451.   FTextRect := Result;
  452.   InflateRect(FTextRect, -1, -1);
  453.   case HintAlignment of
  454.     taCenter: OffsetRect(FTextRect, -1, 0);
  455.     taRightJustify: OffsetRect(FTextRect, -4, 0);
  456.   end;
  457.   FRoundFactor := Max(6, Min(WidthOf(Result), HeightOf(Result)) div 4);
  458.   if HintStyle = hsRoundRect then
  459.     InflateRect(FRect, FRoundFactor div 4, FRoundFactor div 4)
  460.   else if HintStyle = hsEllipse then begin
  461.     X := WidthOf(FRect) / 2;
  462.     Y := HeightOf(FRect) / 2;
  463.     if (X <> 0) and (Y <> 0) then begin
  464.       Factor := Round(Y / 3);
  465.       A := Round(Sqrt((Sqr(X) * Sqr(Y + Factor)) / (Sqr(Y + Factor) - Sqr(Y))));
  466.       InflateRect(FRect, A - Round(X), Round(Factor));
  467.     end;
  468.   end;
  469.   Result := FRect;
  470.   OffsetRect(FRect, -Result.Left, -Result.Top);
  471.   OffsetRect(FTextRect, -Result.Left, -Result.Top);
  472.   Inc(Result.Right, HintShadowSize);
  473.   Inc(Result.Bottom, HintShadowSize);
  474.   if HintTail then begin
  475.     FTileSize.Y := Max(14, Min(WidthOf(FTextRect), HeightOf(FTextRect)) div 2);
  476.     FTileSize.X := FTileSize.Y - 8;
  477.     Inc(Result.Right, FTileSize.X);
  478.     Inc(Result.Bottom, FTileSize.Y);
  479.   end;
  480. end;
  481. {$IFDEF RX_D3}
  482. procedure TRxHintWindow.ActivateHintData(Rect: TRect; const AHint: string;
  483.   AData: Pointer);
  484. begin
  485.   ActivateHint(Rect, AHint);
  486. end;
  487. {$ENDIF}
  488. end.