507 lines
14 KiB
ObjectPascal
507 lines
14 KiB
ObjectPascal
{-----------------------------------------------------------------------------
|
|
The contents of this file are subject to the Mozilla Public License
|
|
Version 1.1 (the "License"); you may not use this file except in compliance
|
|
with the License. You may obtain a copy of the License at
|
|
http://www.mozilla.org/MPL/MPL-1.1.html
|
|
|
|
Software distributed under the License is distributed on an "AS IS" basis,
|
|
WITHOUT WARRANTY OF ANY KIND, either expressed or implied. See the License for
|
|
the specific language governing rights and limitations under the License.
|
|
|
|
The Original Code is: JvHints.PAS, released on 2002-07-04.
|
|
|
|
The Initial Developers of the Original Code are: Fedor Koshevnikov, Igor Pavluk and Serge Korolev
|
|
Copyright (c) 1997, 1998 Fedor Koshevnikov, Igor Pavluk and Serge Korolev
|
|
Copyright (c) 2001,2002 SGB Software
|
|
All Rights Reserved.
|
|
|
|
You may retrieve the latest version of this file at the Project JEDI's JVCL home page,
|
|
located at http://jvcl.sourceforge.net
|
|
|
|
Known Issues:
|
|
-----------------------------------------------------------------------------}
|
|
// $Id: JvHints.pas 10612 2006-05-19 19:04:09Z jfudickar $
|
|
|
|
unit JvHints;
|
|
|
|
{$I jvcl.inc}
|
|
|
|
interface
|
|
|
|
uses
|
|
{$IFDEF UNITVERSIONING}
|
|
JclUnitVersioning,
|
|
{$ENDIF UNITVERSIONING}
|
|
Windows, Messages, Graphics, Controls, Forms, Classes,
|
|
JvTypes;
|
|
|
|
type
|
|
THintStyle = (hsRectangle, hsRoundRect, hsEllipse);
|
|
THintPos = (hpTopRight, hpTopLeft, hpBottomRight, hpBottomLeft);
|
|
THintShadowSize = 0..15;
|
|
|
|
TJvHintWindow = class(THintWindow)
|
|
private
|
|
FSrcImage: TBitmap;
|
|
FImage: TBitmap;
|
|
FPos: THintPos;
|
|
FRect: TRect;
|
|
FTextRect: TRect;
|
|
FTileSize: TPoint;
|
|
FRoundFactor: Integer;
|
|
{$IFDEF VCL}
|
|
procedure WMEraseBkgnd(var Msg: TMessage); message WM_ERASEBKGND;
|
|
procedure WMNCPaint(var Msg: TMessage); message WM_NCPAINT;
|
|
{$ENDIF VCL}
|
|
function CreateRegion(Shade: Boolean): HRGN;
|
|
procedure FillRegion(Rgn: HRGN; Shade: Boolean);
|
|
protected
|
|
procedure CreateParams(var Params: TCreateParams); override;
|
|
procedure Paint; override;
|
|
public
|
|
constructor Create(AOwner: TComponent); override;
|
|
destructor Destroy; override;
|
|
procedure ActivateHint(Rect: TRect;
|
|
const AHint: THintString); override;
|
|
procedure ActivateHintData(Rect: TRect;
|
|
const AHint: THintString; AData: Pointer); override;
|
|
function CalcHintRect(MaxWidth: Integer;
|
|
const AHint: THintString; AData: Pointer): TRect;override;
|
|
end;
|
|
|
|
procedure SetHintStyle(Style: THintStyle; ShadowSize: THintShadowSize;
|
|
Tail: Boolean; Alignment: TAlignment);
|
|
procedure SetStandardHints;
|
|
procedure RegisterHintWindow(AClass: THintWindowClass);
|
|
function GetHintControl: TControl;
|
|
|
|
{$IFDEF UNITVERSIONING}
|
|
const
|
|
UnitVersioning: TUnitVersionInfo = (
|
|
RCSfile: '$URL: https://jvcl.svn.sourceforge.net/svnroot/jvcl/tags/JVCL3_32/run/JvHints.pas $';
|
|
Revision: '$Revision: 10612 $';
|
|
Date: '$Date: 2006-05-19 21:04:09 +0200 (ven., 19 mai 2006) $';
|
|
LogPath: 'JVCL\run'
|
|
);
|
|
{$ENDIF UNITVERSIONING}
|
|
|
|
implementation
|
|
|
|
uses
|
|
SysUtils, Math,
|
|
JvJCLUtils;
|
|
|
|
var
|
|
HintStyle: THintStyle = hsRectangle;
|
|
HintShadowSize: Integer = 0;
|
|
HintTail: Boolean = False;
|
|
HintAlignment: TAlignment = taLeftJustify;
|
|
|
|
procedure RegisterHintWindow(AClass: THintWindowClass);
|
|
begin
|
|
HintWindowClass := AClass;
|
|
with Application do
|
|
if ShowHint then
|
|
begin
|
|
ShowHint := False;
|
|
ShowHint := True;
|
|
end;
|
|
end;
|
|
|
|
procedure SetStandardHints;
|
|
begin
|
|
RegisterHintWindow(THintWindow);
|
|
end;
|
|
|
|
procedure SetHintStyle(Style: THintStyle; ShadowSize: THintShadowSize;
|
|
Tail: Boolean; Alignment: TAlignment);
|
|
begin
|
|
HintStyle := Style;
|
|
HintShadowSize := ShadowSize;
|
|
HintTail := Tail;
|
|
HintAlignment := Alignment;
|
|
RegisterHintWindow(TJvHintWindow);
|
|
end;
|
|
|
|
function GetHintControl: TControl;
|
|
var
|
|
CursorPos: TPoint;
|
|
begin
|
|
GetCursorPos(CursorPos);
|
|
Result := FindDragTarget(CursorPos, True);
|
|
while (Result <> nil) and not Result.ShowHint do
|
|
Result := Result.Parent;
|
|
if (Result <> nil) and (csDesigning in Result.ComponentState) then
|
|
Result := nil;
|
|
end;
|
|
|
|
{$IFDEF VCL}
|
|
procedure StandardHintFont(AFont: TFont);
|
|
var
|
|
NonClientMetrics: TNonClientMetrics;
|
|
begin
|
|
NonClientMetrics.cbSize := SizeOf(NonClientMetrics);
|
|
if SystemParametersInfo(SPI_GETNONCLIENTMETRICS, 0, @NonClientMetrics, 0) then
|
|
AFont.Handle := CreateFontIndirect(NonClientMetrics.lfStatusFont)
|
|
else
|
|
begin
|
|
AFont.Name := 'MS Sans Serif';
|
|
AFont.Size := 8;
|
|
end;
|
|
AFont.Color := clInfoText;
|
|
end;
|
|
{$ENDIF VCL}
|
|
|
|
{$IFDEF VisualCLX}
|
|
procedure StandardHintFont(AFont: TFont);
|
|
begin
|
|
AFont.Name := 'Helvetica';
|
|
AFont.Height := 11;
|
|
AFont.Color := clInfoText;
|
|
end;
|
|
{$ENDIF VisualCLX}
|
|
|
|
constructor TJvHintWindow.Create(AOwner: TComponent);
|
|
begin
|
|
inherited Create(AOwner);
|
|
StandardHintFont(Canvas.Font);
|
|
FImage := TBitmap.Create;
|
|
FSrcImage := TBitmap.Create;
|
|
end;
|
|
|
|
destructor TJvHintWindow.Destroy;
|
|
begin
|
|
FSrcImage.Free;
|
|
FImage.Free;
|
|
inherited Destroy;
|
|
end;
|
|
|
|
{$IFDEF VCL}
|
|
|
|
procedure TJvHintWindow.CreateParams(var Params: TCreateParams);
|
|
begin
|
|
inherited CreateParams(Params);
|
|
Params.Style := Params.Style and not WS_BORDER;
|
|
end;
|
|
|
|
procedure TJvHintWindow.WMNCPaint(var Msg: TMessage);
|
|
begin
|
|
end;
|
|
|
|
procedure TJvHintWindow.WMEraseBkgnd(var Msg: TMessage);
|
|
begin
|
|
Msg.Result := 1;
|
|
end;
|
|
|
|
{$ENDIF VCL}
|
|
|
|
function TJvHintWindow.CreateRegion(Shade: Boolean): HRGN;
|
|
var
|
|
R: TRect;
|
|
W, TileOffs: Integer;
|
|
Tail, Dest: HRGN;
|
|
P: TPoint;
|
|
|
|
function CreatePolyRgn(const Points: array of TPoint): HRGN;
|
|
begin
|
|
Result := CreatePolygonRgn(Points[0], High(Points) + 1, WINDING);
|
|
end;
|
|
|
|
begin
|
|
R := FRect;
|
|
Result := 0;
|
|
if Shade then
|
|
OffsetRect(R, HintShadowSize, HintShadowSize);
|
|
case HintStyle of
|
|
hsRoundRect:
|
|
Result := CreateRoundRectRgn(R.Left, R.Top, R.Right, R.Bottom,
|
|
FRoundFactor, FRoundFactor);
|
|
hsEllipse:
|
|
Result := CreateEllipticRgnIndirect(R);
|
|
hsRectangle:
|
|
Result := CreateRectRgnIndirect(R);
|
|
end;
|
|
if HintTail then
|
|
begin
|
|
R := FTextRect;
|
|
GetCursorPos(P);
|
|
TileOffs := 0;
|
|
if FPos in [hpTopLeft, hpBottomLeft] then
|
|
TileOffs := Width;
|
|
if Shade then
|
|
begin
|
|
OffsetRect(R, HintShadowSize, HintShadowSize);
|
|
Inc(TileOffs, HintShadowSize);
|
|
end;
|
|
W := Min(Max(8, Min(RectWidth(R), RectHeight(R)) div 4), RectWidth(R) div 2);
|
|
case FPos of
|
|
hpTopRight:
|
|
Tail := CreatePolyRgn([Point(TileOffs, Height - HintShadowSize),
|
|
Point(R.Left + W div 4, R.Bottom), Point(R.Left + 2 * W, R.Bottom)]);
|
|
hpTopLeft:
|
|
Tail := CreatePolyRgn([Point(TileOffs, Height - HintShadowSize),
|
|
Point(R.Right - W div 4, R.Bottom), Point(R.Right - 2 * W, R.Bottom)]);
|
|
hpBottomRight:
|
|
Tail := CreatePolyRgn([Point(TileOffs, 0),
|
|
Point(R.Left + W div 4, R.Top), Point(R.Left + 2 * W, R.Top)]);
|
|
else
|
|
Tail := CreatePolyRgn([Point(TileOffs, 0),
|
|
Point(R.Right - W div 4, R.Top), Point(R.Right - 2 * W, R.Top)]);
|
|
end;
|
|
try
|
|
Dest := Result;
|
|
Result := CreateRectRgnIndirect(R);
|
|
try
|
|
CombineRgn(Result, Dest, Tail, RGN_OR);
|
|
finally
|
|
if Dest <> 0 then
|
|
DeleteObject(Dest);
|
|
end;
|
|
finally
|
|
DeleteObject(Tail);
|
|
end;
|
|
end;
|
|
end;
|
|
|
|
procedure TJvHintWindow.FillRegion(Rgn: HRGN; Shade: Boolean);
|
|
begin
|
|
if Shade then
|
|
begin
|
|
FImage.Canvas.Brush.Bitmap :=
|
|
AllocPatternBitmap(clBtnFace, clWindowText);
|
|
FImage.Canvas.Pen.Style := psClear;
|
|
end
|
|
else
|
|
begin
|
|
FImage.Canvas.Pen.Style := psSolid;
|
|
FImage.Canvas.Brush.Color := Color;
|
|
end;
|
|
try
|
|
PaintRgn(FImage.Canvas.Handle, Rgn);
|
|
if not Shade then
|
|
begin
|
|
FImage.Canvas.Brush.Color := Font.Color;
|
|
if (HintStyle = hsRectangle) and not HintTail then
|
|
DrawEdge(FImage.Canvas.Handle, FRect, BDR_RAISEDOUTER, BF_RECT)
|
|
else
|
|
FrameRgn(FImage.Canvas.Handle, Rgn, FImage.Canvas.Brush.Handle, 1, 1);
|
|
end;
|
|
finally
|
|
if Shade then
|
|
begin
|
|
FImage.Canvas.Brush.Bitmap := nil;
|
|
FImage.Canvas.Pen.Style := psSolid;
|
|
end;
|
|
FImage.Canvas.Brush.Color := Color;
|
|
end;
|
|
end;
|
|
|
|
procedure TJvHintWindow.Paint;
|
|
var
|
|
R: TRect;
|
|
FShadeRgn, FRgn: HRGN;
|
|
|
|
procedure PaintText(R: TRect);
|
|
const
|
|
Flag: array [TAlignment] of Longint = (DT_LEFT, DT_RIGHT, DT_CENTER);
|
|
begin
|
|
DrawText(FImage.Canvas, Caption, -1, R,
|
|
DT_NOPREFIX or DT_WORDBREAK or Flag[HintAlignment]);
|
|
end;
|
|
|
|
begin
|
|
R := ClientRect;
|
|
FImage.Handle := CreateCompatibleBitmap(Canvas.Handle,
|
|
RectWidth(ClientRect), RectHeight(ClientRect));
|
|
FImage.Canvas.Font := Self.Canvas.Font;
|
|
if (HintStyle <> hsRectangle) or (HintShadowSize > 0) or HintTail then
|
|
FImage.Canvas.Draw(0, 0, FSrcImage);
|
|
FRgn := CreateRegion(False);
|
|
FShadeRgn := CreateRegion(True);
|
|
try
|
|
FillRegion(FShadeRgn, True);
|
|
FillRegion(FRgn, False);
|
|
finally
|
|
DeleteObject(FShadeRgn);
|
|
DeleteObject(FRgn);
|
|
end;
|
|
R := FTextRect;
|
|
if HintAlignment = taLeftJustify then
|
|
Inc(R.Left, 2);
|
|
PaintText(R);
|
|
Canvas.Draw(0, 0, FImage);
|
|
end;
|
|
|
|
procedure TJvHintWindow.ActivateHint(Rect: TRect;
|
|
const AHint: THintString);
|
|
var
|
|
R: TRect;
|
|
ScreenDC: HDC;
|
|
P: TPoint;
|
|
begin
|
|
Caption := AHint;
|
|
GetCursorPos(P);
|
|
FPos := hpBottomRight;
|
|
R := CalcHintRect(Screen.Width, AHint, nil);
|
|
OffsetRect(R, Rect.Left - R.Left, Rect.Top - R.Top);
|
|
Rect := R;
|
|
BoundsRect := Rect;
|
|
|
|
if HintTail then
|
|
begin
|
|
Rect.Top := P.Y - Height - 3;
|
|
if Rect.Top < 0 then
|
|
Rect.Top := BoundsRect.Top
|
|
else
|
|
Rect.Bottom := Rect.Top + RectHeight(BoundsRect);
|
|
|
|
Rect.Left := P.X + 1;
|
|
if Rect.Left < 0 then
|
|
Rect.Left := BoundsRect.Left
|
|
else
|
|
Rect.Right := Rect.Left + RectWidth(BoundsRect);
|
|
end;
|
|
|
|
if Rect.Top + Height > Screen.Height then
|
|
begin
|
|
Rect.Top := Screen.Height - Height;
|
|
if Rect.Top <= P.Y then
|
|
Rect.Top := P.Y - Height - 3;
|
|
end;
|
|
if Rect.Left + Width > Screen.Width then
|
|
begin
|
|
Rect.Left := Screen.Width - Width;
|
|
if Rect.Left <= P.X then
|
|
Rect.Left := P.X - Width - 3;
|
|
end;
|
|
if Rect.Left < 0 then
|
|
begin
|
|
Rect.Left := 0;
|
|
if Rect.Left + Width >= P.X then
|
|
Rect.Left := P.X - Width - 1;
|
|
end;
|
|
if Rect.Top < 0 then
|
|
begin
|
|
Rect.Top := 0;
|
|
if Rect.Top + Height >= P.Y then
|
|
Rect.Top := P.Y - Height - 1;
|
|
end;
|
|
|
|
if (HintStyle <> hsRectangle) or (HintShadowSize > 0) or HintTail then
|
|
begin
|
|
FPos := hpBottomRight;
|
|
if Rect.Top + Height < P.Y then
|
|
FPos := hpTopRight;
|
|
if Rect.Left + Width < P.X then
|
|
begin
|
|
if FPos = hpBottomRight then
|
|
FPos := hpBottomLeft
|
|
else
|
|
FPos := hpTopLeft;
|
|
end;
|
|
if HintTail then
|
|
begin
|
|
if FPos in [hpBottomRight, hpBottomLeft] then
|
|
begin
|
|
OffsetRect(FRect, 0, FTileSize.Y);
|
|
OffsetRect(FTextRect, 0, FTileSize.Y);
|
|
end;
|
|
if FPos in [hpBottomRight, hpTopRight] then
|
|
begin
|
|
OffsetRect(FRect, FTileSize.X, 0);
|
|
OffsetRect(FTextRect, FTileSize.X, 0);
|
|
end;
|
|
end;
|
|
if HandleAllocated then
|
|
begin
|
|
SetWindowPos(Handle, HWND_BOTTOM, 0, 0, 0, 0,
|
|
SWP_HIDEWINDOW or SWP_NOACTIVATE or SWP_NOSIZE or SWP_NOMOVE);
|
|
if Screen.ActiveForm <> nil then
|
|
UpdateWindow(Screen.ActiveForm.Handle);
|
|
end;
|
|
ScreenDC := GetDC(HWND_DESKTOP);
|
|
try
|
|
with FSrcImage do
|
|
begin
|
|
Width := RectWidth(BoundsRect);
|
|
Height := RectHeight(BoundsRect);
|
|
BitBlt(Canvas.Handle, 0, 0, Width, Height, ScreenDC,
|
|
Rect.Left, Rect.Top, SRCCOPY);
|
|
end;
|
|
finally
|
|
ReleaseDC(HWND_DESKTOP, ScreenDC);
|
|
end;
|
|
end;
|
|
SetWindowPos(Handle, HWND_TOPMOST, Rect.Left, Rect.Top, 0, 0,
|
|
SWP_SHOWWINDOW or SWP_NOACTIVATE or SWP_NOSIZE);
|
|
end;
|
|
|
|
function TJvHintWindow.CalcHintRect(MaxWidth: Integer;
|
|
const AHint: THintString;
|
|
AData: Pointer): TRect;
|
|
const
|
|
Flag: array [TAlignment] of Longint = (DT_LEFT, DT_RIGHT, DT_CENTER);
|
|
var
|
|
A: Integer;
|
|
X, Y, Factor: Double;
|
|
begin
|
|
Result := Rect(0, 0, MaxWidth, 0);
|
|
DrawText(Canvas, AHint, -1, Result,
|
|
DT_CALCRECT or DT_WORDBREAK or DT_NOPREFIX or Flag[HintAlignment] or {$IFDEF VCL} DrawTextBiDiModeFlagsReadingOnly {$ENDIF});
|
|
Inc(Result.Right, 8);
|
|
Inc(Result.Bottom, 4);
|
|
FRect := Result;
|
|
FTextRect := Result;
|
|
InflateRect(FTextRect, -1, -1);
|
|
case HintAlignment of
|
|
taCenter:
|
|
OffsetRect(FTextRect, -1, 0);
|
|
taRightJustify:
|
|
OffsetRect(FTextRect, -4, 0);
|
|
end;
|
|
FRoundFactor := Max(6, Min(RectWidth(Result), RectHeight(Result)) div 4);
|
|
if HintStyle = hsRoundRect then
|
|
InflateRect(FRect, FRoundFactor div 4, FRoundFactor div 4)
|
|
else
|
|
if HintStyle = hsEllipse then
|
|
begin
|
|
X := RectWidth(FRect) / 2;
|
|
Y := RectHeight(FRect) / 2;
|
|
if (X <> 0) and (Y <> 0) then
|
|
begin
|
|
Factor := Round(Y / 3);
|
|
A := Round(Sqrt((Sqr(X) * Sqr(Y + Factor)) / (Sqr(Y + Factor) - Sqr(Y))));
|
|
InflateRect(FRect, A - Round(X), Round(Factor));
|
|
end;
|
|
end;
|
|
Result := FRect;
|
|
OffsetRect(FRect, -Result.Left, -Result.Top);
|
|
OffsetRect(FTextRect, -Result.Left, -Result.Top);
|
|
Inc(Result.Right, HintShadowSize);
|
|
Inc(Result.Bottom, HintShadowSize);
|
|
if HintTail then
|
|
begin
|
|
FTileSize.Y := Max(14, Min(RectWidth(FTextRect), RectHeight(FTextRect)) div 2);
|
|
FTileSize.X := FTileSize.Y - 8;
|
|
Inc(Result.Right, FTileSize.X);
|
|
Inc(Result.Bottom, FTileSize.Y);
|
|
end;
|
|
end;
|
|
|
|
procedure TJvHintWindow.ActivateHintData(Rect: TRect;
|
|
const AHint: THintString; AData: Pointer);
|
|
begin
|
|
ActivateHint(Rect, AHint);
|
|
end;
|
|
|
|
{$IFDEF UNITVERSIONING}
|
|
initialization
|
|
RegisterUnitVersion(HInstance, UnitVersioning);
|
|
|
|
finalization
|
|
UnregisterUnitVersion(HInstance);
|
|
{$ENDIF UNITVERSIONING}
|
|
|
|
end.
|
|
|