{----------------------------------------------------------------------------- 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: JvgHint.PAS, released on 2003-01-15. The Initial Developer of the Original Code is Andrey V. Chudin, [chudin att yandex dott ru] Portions created by Andrey V. Chudin are Copyright (C) 2003 Andrey V. Chudin. All Rights Reserved. Contributor(s): Michael Beck [mbeck att bigfoot dott com]. Rob den Braasem [rbraasem att xs4all dott nl]. 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: JvgHint.pas 10612 2006-05-19 19:04:09Z jfudickar $ unit JvgHint; {$I jvcl.inc} interface uses {$IFDEF USEJVCL} {$IFDEF UNITVERSIONING} JclUnitVersioning, {$ENDIF UNITVERSIONING} {$ENDIF USEJVCL} Windows, Messages, SysUtils, Graphics, Controls, Classes, Forms, {$IFDEF USEJVCL} JvComponentBase, {$ENDIF USEJVCL} JvgCommClasses; type {$IFDEF USEJVCL} TJvgHint = class(TJvComponent) {$ELSE} TJvgHint = class(TComponent) {$ENDIF USEJVCL} private FOnShowHint: TShowHintEvent; FOnHint: TNotifyEvent; FActive: Boolean; FOnHintOld: TNotifyEvent; FOnShowHintOld: TShowHintEvent; FShowHint: Boolean; FHintWindow: THintWindow; FHintControl: TControl; FGlyph: TBitmap; FHintStyle: TJvgHintStyle; FSpacing: Integer; FGlyphAlign: TJvg2DAlign; FAlignment: TAlignment; procedure SetGlyph(const Value: TBitmap); procedure NewHint(Sender: TObject); procedure NewShowHint(var HintStr: string; var CanShow: Boolean; var HintInfo: THintInfo); protected procedure Notification(Component: TComponent; Operation: TOperation); override; procedure Loaded; override; procedure InitHint; public constructor Create(AOwner: TComponent); override; destructor Destroy; override; procedure ShowHintAt(X, Y: Integer; const Caption: string); published property Active: Boolean read FActive write FActive default False; property ShowHint: Boolean read FShowHint write FShowHint default False; property Glyph: TBitmap read FGlyph write SetGlyph; property Style: TJvgHintStyle read FHintStyle write FHintStyle; property Spacing: Integer read FSpacing write FSpacing default 0; property GlyphAlign: TJvg2DAlign read FGlyphAlign write FGlyphAlign; property Alignment: TAlignment read FAlignment write FAlignment default taLeftJustify; property OnShowHint: TShowHintEvent read FOnShowHint write FOnShowHint; property OnHint: TNotifyEvent read FOnHint write FOnHint; end; {$IFDEF USEJVCL} {$IFDEF UNITVERSIONING} const UnitVersioning: TUnitVersionInfo = ( RCSfile: '$URL: https://jvcl.svn.sourceforge.net/svnroot/jvcl/tags/JVCL3_32/run/JvgHint.pas $'; Revision: '$Revision: 10612 $'; Date: '$Date: 2006-05-19 21:04:09 +0200 (ven., 19 mai 2006) $'; LogPath: 'JVCL\run' ); {$ENDIF UNITVERSIONING} {$ENDIF USEJVCL} implementation uses Math, ExtCtrls, {$IFDEF USEJVCL} JvResources, JvConsts, {$ENDIF USEJVCL} JvgTypes, JvgUtils; {$R JvgHint.res} {$IFNDEF USEJVCL} resourcestring RsEOnlyOneInstanceOfTJvgHint = 'Cannot create more than one instance of TJvgHint component'; {$ENDIF !USEJVCL} type TJvgHintWindow = class(THintWindow) private FHintComponent: TJvgHint; protected procedure CreateParams(var Params: TCreateParams); override; procedure WMKillFocus(var Msg: TMessage); message WM_ACTIVATE; procedure Paint; override; public constructor Create(AOwner: TComponent); override; procedure ActivateHint(Rect: TRect; const AHint: string); override; function CalcHintRect(MaxWidth: Integer; const AHint: string; AData: Pointer): TRect; override; end; var lpFrHintComponent: TJvgHint; //=== { TJvgHint } =========================================================== constructor TJvgHint.Create(AOwner: TComponent); begin inherited Create(AOwner); FGlyph := TBitmap.Create; FHintStyle := TJvgHintStyle.Create; FGlyphAlign := TJvg2DAlign.Create; FActive := False; FShowHint := False; FSpacing := 0; FAlignment := taLeftJustify; FHintStyle.Color := clWindow; FHintStyle.Bevel.Inner := bvRaised; FHintStyle.Bevel.Outer := bvLowered; if not (csDesigning in ComponentState) then InitHint; Application.ShowHint := False; Application.ShowHint := True; end; destructor TJvgHint.Destroy; begin FGlyph.Free; FHintStyle.Free; FGlyphAlign.Free; if Assigned(FOnHintOld) then begin Application.OnShowHint := FOnShowHintOld; Application.OnHint := FOnHintOld; end; inherited Destroy; end; procedure TJvgHint.Notification(Component: TComponent; Operation: TOperation); begin if (Component <> Self) and (Operation = opInsert) and (Component is TJvgHint) then raise Exception.CreateRes(@RsEOnlyOneInstanceOfTJvgHint); end; procedure TJvgHint.InitHint; begin with Application do begin FOnHintOld := OnHint; FOnShowHintOld := OnShowHint; OnShowHint := NewShowHint; OnHint := NewHint; HintWindowClass := TJvgHintWindow; lpFrHintComponent := Self; end; FShowHint := True; end; procedure TJvgHint.Loaded; begin inherited Loaded; if not (csDesigning in ComponentState) and Active then begin InitHint; Application.ShowHint := False; Application.ShowHint := ShowHint; end; if Glyph.Empty then begin Glyph.Assign(nil); // fixes GDI resource leak Glyph.LoadFromResourceName(HInstance, 'JvgHintHELP'); end; end; procedure TJvgHint.NewHint(Sender: TObject); begin if Assigned(FOnHint) then FOnHint(Sender); end; procedure TJvgHint.NewShowHint(var HintStr: string; var CanShow: Boolean; var HintInfo: THintInfo); begin FHintControl := HintInfo.HintControl; if Assigned(FOnShowHint) then FOnShowHint(HintStr, CanShow, HintInfo); if CanShow then Self.ShowHintAt(HintInfo.CursorPos.X, HintInfo.CursorPos.Y, HintStr); end; procedure TJvgHint.ShowHintAt(X, Y: Integer; const Caption: string); var R: TRect; HW: TJvgHintWindow; begin HW := TJvgHintWindow.Create(Application); R := Bounds(X, Y, 10, 10); Windows.DrawText(HW.Canvas.Handle, PChar(Caption), Length(Caption), R, DT_WORDBREAK or DT_CALCRECT); HW.ActivateHint(R, Caption); end; //=== { TJvgHintWindow } ===================================================== constructor TJvgHintWindow.Create(AOwner: TComponent); begin inherited Create(AOwner); FHintComponent := TJvgHint(lpFrHintComponent); try if Assigned(FHintComponent) then FHintComponent.FHintWindow := Self; with Canvas do begin Font.Assign(FHintComponent.Style.Font); {$IFDEF GL_RUS} Font.CharSet := RUSSIAN_CHARSET; {$ENDIF GL_RUS} end; except end; end; procedure TJvgHintWindow.CreateParams(var Params: TCreateParams); begin inherited CreateParams(Params); Params.Style := Params.Style and not WS_BORDER; end; procedure TJvgHintWindow.ActivateHint(Rect: TRect; const AHint: string); begin Caption := AHint; BoundsRect := Rect; Tag := 1; Width := Width + 20; Height := Height + 1; if Rect.Top + Height > Screen.Height then Rect.Top := Screen.Height - Height; if Rect.Left + Width > Screen.Width then Rect.Left := Screen.Width - Width; if Rect.Left < 0 then Rect.Left := 0; if Rect.Bottom < 0 then Rect.Bottom := 0; SetWindowPos(Handle, HWND_TOPMOST, Rect.Left, Rect.Top, Width, Height, SWP_SHOWWINDOW or SWP_NOACTIVATE); end; procedure TJvgHintWindow.Paint; const cAlignments: array [TAlignment] of Longint = (DT_LEFT, DT_RIGHT, DT_CENTER); var R: TRect; GlyphX, GlyphY: Integer; begin R := ClientRect; Dec(R.Right); Dec(R.Bottom); Canvas.Brush.Color := clWhite; Canvas.Pen.Color := 0; GlyphX := 0; GlyphY := 0; with FHintComponent.Style do begin R := DrawBoxEx(Canvas.Handle, R, Bevel.Sides, Bevel.Inner, Bevel.Outer, Bevel.Bold, Color, Gradient.Active); if Gradient.Active then begin Inc(R.Right); Inc(R.Bottom); Gradient.Draw(Canvas.Handle, R, Integer(psSolid), 1); Dec(R.Right); Dec(R.Bottom); end; end; if Assigned(FHintComponent) then begin case FHintComponent.GlyphAlign.Vertical of fvaTop: GlyphY := R.Top; fvaCenter: GlyphY := (R.Bottom - R.Top - FHintComponent.Glyph.Height) div 2; fvaBottom: GlyphY := R.Bottom - FHintComponent.Glyph.Height; end; case FHintComponent.GlyphAlign.Horizontal of fhaLeft: GlyphX := R.Left + 1; fhaCenter: GlyphX := (R.Right - R.Left - FHintComponent.Glyph.Width) div 2; fhaRight: GlyphX := R.Right - FHintComponent.Glyph.Width - 2; end; CreateBitmapExt(Canvas.Handle, FHintComponent.Glyph, R, GlyphX, GlyphY, fwoNone, fdsDefault, True, GetTransparentColor(FHintComponent.Glyph, ftcLeftBottomPixel), 0); case FHintComponent.GlyphAlign.Horizontal of fhaLeft: Inc(R.Left, FHintComponent.Glyph.Width + FHintComponent.Spacing); fhaCenter: { nothing }; fhaRight: Dec(R.Right, FHintComponent.Glyph.Width + FHintComponent.Spacing); end; end; SetBkMode(Canvas.Handle, TRANSPARENT); Canvas.Font.Assign(FHintComponent.Style.Font); InflateRect(R, -1, -1); if ClientRect.Bottom - ClientRect.Top > Canvas.TextHeight('Y') * 2 then Windows.DrawText(Canvas.Handle, PChar(Caption), Length(Caption), R, DT_VCENTER or DT_WORDBREAK or cAlignments[FHintComponent.Alignment]) else Windows.DrawText(Canvas.Handle, PChar(Caption), Length(Caption), R, DT_VCENTER or DT_SINGLELINE or cAlignments[FHintComponent.Alignment]); end; procedure TJvgHintWindow.WMKillFocus(var Msg: TMessage); begin Hide; end; function TJvgHintWindow.CalcHintRect(MaxWidth: Integer; const AHint: string; AData: Pointer): TRect; begin Canvas.Font.Assign(FHintComponent.Style.Font); Result := inherited CalcHintRect(MaxWidth, AHint, AData); if Assigned(FHintComponent.Glyph) and not FHintComponent.Glyph.Empty then begin Result.Bottom := Max(Result.Bottom, FHintComponent.Glyph.Height); Inc(Result.Right, FHintComponent.Glyph.Width + FHintComponent.Spacing); end; Inc(Result.Bottom, FHintComponent.Style.Bevel.BordersHeight); Inc(Result.Right, FHintComponent.Style.Bevel.BordersWidth); end; procedure TJvgHint.SetGlyph(const Value: TBitmap); begin FGlyph.Assign(Value); end; {$IFDEF USEJVCL} {$IFDEF UNITVERSIONING} initialization RegisterUnitVersion(HInstance, UnitVersioning); finalization UnregisterUnitVersion(HInstance); {$ENDIF UNITVERSIONING} {$ENDIF USEJVCL} end.