git-svn-id: https://192.168.0.254/svn/Componentes.Terceros.jvcl@19 7f62d464-2af8-f54e-996c-e91b33f51cbe
2843 lines
82 KiB
ObjectPascal
2843 lines
82 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: JvCaptionButton.PAS, released on 2002-05-26.
|
|
|
|
The Initial Developer of the Original Code is Peter Thörnqvist [peter3 at sourceforge dot net]
|
|
Portions created by Peter Thörnqvist are Copyright (C) 2002 Peter Thörnqvist.
|
|
|
|
This unit is a merging of the original TJvCaptionButton, TJvaCaptionButton.
|
|
Merging done 2003-06-12 by Remko Bonte [remkobonte at myrealbox dot com]
|
|
|
|
All Rights Reserved.
|
|
|
|
Contributor(s):
|
|
Andrei Prygounkov <a dot prygounkov at gmx dot de>, author of TJvaCaptionButton.
|
|
Remko Bonte [remkobonte at myrealbox dot com], theme support, actions
|
|
Olivier Sannier [obones att altern dott org], caption hints.
|
|
|
|
You may retrieve the latest version of this file at the Project JEDI's JVCL home page,
|
|
located at http://jvcl.delphi-jedi.org
|
|
|
|
Modified 2003-06-13 (p3):
|
|
- Fixed MouseUp X,Y inconsistentcy (did not report the same values as MouseDown)
|
|
- Added MouseMove handler
|
|
- Added ShowHint, ParentShowHint
|
|
- Fixed drawing of disabled MinimizeToTray icon as well as incorrect Font.Color in text drawing
|
|
- Added Assign
|
|
- Tested on W2k
|
|
- Demo (examples\CaptionBtn) updated and extended
|
|
|
|
Known Issues:
|
|
|
|
* Msimg32.dll code should be moved to seperate import unit. Code is partly
|
|
copied from JwaWinGDI.pas.
|
|
* Button can disappear at design-time when switching themes.
|
|
* With more buttons, button can appear hot while mouse is over another caption
|
|
button.
|
|
* Still some flicker while resizing due to wrong FButtonRect, see comment
|
|
at HandleNCPaintBefore.
|
|
* Buttons on small caption (BorderStyle in [bsSizeToolWin, bsToolWin]) looks
|
|
ugly.
|
|
|
|
-----------------------------------------------------------------------------}
|
|
// $Id: JvCaptionButton.pas 12579 2009-10-26 19:59:53Z ahuser $
|
|
|
|
unit JvCaptionButton;
|
|
|
|
{$I jvcl.inc}
|
|
{$I windowsonly.inc}
|
|
|
|
interface
|
|
|
|
uses
|
|
{$IFDEF UNITVERSIONING}
|
|
JclUnitVersioning,
|
|
{$ENDIF UNITVERSIONING}
|
|
Windows, Messages, Classes, Graphics, Controls, Forms, Types,
|
|
ActnList, ImgList,
|
|
JvComponentBase, JvTypes, JvWin32;
|
|
|
|
type
|
|
TJvStandardButton = (tsbNone, tsbClose, tsbHelp, tsbMax, tsbMin, tsbRestore,
|
|
tsbMinimizeToTray); // a la e-Mule
|
|
TJvCaptionButtonLayout = (cbImageLeft, cbImageRight);
|
|
TJvRedrawKind = (rkDirect, rkIndirect, rkTotalCaptionBar);
|
|
|
|
TJvCaptionButton = class;
|
|
|
|
TJvCaptionButtonActionLink = class(TActionLink)
|
|
protected
|
|
FClient: TJvCaptionButton;
|
|
procedure AssignClient(AClient: TObject); override;
|
|
function IsCaptionLinked: Boolean; override;
|
|
function IsEnabledLinked: Boolean; override;
|
|
function IsHintLinked: Boolean; override;
|
|
function IsImageIndexLinked: Boolean; override;
|
|
function IsVisibleLinked: Boolean; override;
|
|
function IsOnExecuteLinked: Boolean; override;
|
|
|
|
procedure SetCaption(const Value: string); override;
|
|
procedure SetEnabled(Value: Boolean); override;
|
|
procedure SetHint(const Value: string); override;
|
|
procedure SetImageIndex(Value: Integer); override;
|
|
procedure SetVisible(Value: Boolean); override;
|
|
procedure SetOnExecute(Value: TNotifyEvent); override;
|
|
end;
|
|
|
|
TJvCaptionButtonActionLinkClass = class of TJvCaptionButtonActionLink;
|
|
|
|
TJvCaptionButton = class(TJvComponent)
|
|
private
|
|
{ Properties }
|
|
FAlignment: TAlignment;
|
|
FHeight: Integer;
|
|
FLeft: Integer;
|
|
FTop: Integer;
|
|
FWidth: Integer;
|
|
FCaption: string;
|
|
FDown: Boolean;
|
|
FEnabled: Boolean;
|
|
FFont: TFont;
|
|
FHint: string;
|
|
FImageIndex: TImageIndex;
|
|
FImages: TCustomImageList;
|
|
FLayout: TJvCaptionButtonLayout;
|
|
FMargin: Integer;
|
|
FPosition: Integer;
|
|
FSpacing: Integer;
|
|
FStandard: TJvStandardButton;
|
|
FToggle: Boolean;
|
|
FVisible: Boolean;
|
|
FOnClick: TNotifyEvent;
|
|
FOnMouseUp: TMouseEvent;
|
|
FOnMouseDown: TMouseEvent;
|
|
FOnMouseMove: TMouseMoveEvent;
|
|
|
|
FDefaultButtonLeft: Integer;
|
|
FDefaultButtonTop: Integer;
|
|
FDefaultButtonWidth: Integer;
|
|
FDefaultButtonHeight: Integer;
|
|
|
|
FActionLink: TJvCaptionButtonActionLink;
|
|
FBuffer: TBitmap;
|
|
FButtonRect: TRect;
|
|
FCaptionHeight: Integer;
|
|
FClickRect: TRect; // Clickable area is a bit bigger than the button
|
|
FHasCaption: Boolean; // True, if the form has a caption
|
|
FHasSmallCaption: Boolean; // True, if the form has BorderStyle bsToolWindow, bsSizeToolWin
|
|
FImageChangeLink: TChangeLink;
|
|
FMouseButtonDown: Boolean;
|
|
FMouseInControl: Boolean;
|
|
FNeedRecalculate: Boolean;
|
|
FRgnChanged: Boolean;
|
|
FSaveRgn: HRGN;
|
|
FShowHint: Boolean;
|
|
FParentShowHint: Boolean;
|
|
{tool tip specific}
|
|
FToolTipHandle: THandle;
|
|
{tool tip specific end}
|
|
FCurrentWindowState: TWindowState;
|
|
|
|
{$IFDEF JVCLThemesEnabled}
|
|
FCaptionActive: Boolean;
|
|
FForceDrawSimple: Boolean;
|
|
FForceRedraw: Boolean;
|
|
function GetIsThemed: Boolean;
|
|
procedure SetForceDrawSimple(const Value: Boolean);
|
|
{$ENDIF JVCLThemesEnabled}
|
|
function GetAction: TBasicAction;
|
|
function GetIsImageVisible: Boolean;
|
|
function GetParentForm: TCustomForm;
|
|
function GetParentFormHandle: THandle;
|
|
function IsCaptionStored: Boolean;
|
|
function IsEnabledStored: Boolean;
|
|
function IsHintStored: Boolean;
|
|
function IsImageIndexStored: Boolean;
|
|
procedure SetAction(const Value: TBasicAction);
|
|
procedure SetAlignment(Value: TAlignment);
|
|
procedure SetCaption(Value: string);
|
|
procedure SetDown(const Value: Boolean);
|
|
procedure SetEnabled(const Value: Boolean);
|
|
procedure SetFont(Value: TFont);
|
|
procedure SetHeight(Value: Integer);
|
|
procedure SetImageIndex(const Value: TImageIndex);
|
|
procedure SetImages(const Value: TCustomImageList);
|
|
procedure SetLayout(const Value: TJvCaptionButtonLayout);
|
|
procedure SetLeft(Value: Integer);
|
|
procedure SetMargin(const Value: Integer);
|
|
procedure SetMouseInControl(const Value: Boolean);
|
|
procedure SetPosition(const Value: Integer);
|
|
procedure SetSpacing(const Value: Integer);
|
|
procedure SetStandard(Value: TJvStandardButton);
|
|
procedure SetToggle(const Value: Boolean);
|
|
procedure SetTop(Value: Integer);
|
|
procedure SetVisible(const Value: Boolean);
|
|
procedure SetWidth(Value: Integer);
|
|
|
|
{tool tip handling}
|
|
procedure CreateToolTip(Wnd: THandle);
|
|
procedure DestroyToolTip;
|
|
procedure HideToolTip;
|
|
procedure ForwardToToolTip(Msg: TMessage);
|
|
|
|
procedure Hook;
|
|
procedure UnHook;
|
|
function WndProcAfter(var Msg: TMessage): Boolean;
|
|
function WndProcBefore(var Msg: TMessage): Boolean;
|
|
|
|
procedure DrawButton(DC: HDC);
|
|
{$IFDEF JVCLThemesEnabled}
|
|
procedure DrawButtonBackground(ACanvas: TCanvas);
|
|
{$ENDIF JVCLThemesEnabled}
|
|
procedure DrawStandardButton(ACanvas: TCanvas);
|
|
procedure DrawNonStandardButton(ACanvas: TCanvas);
|
|
procedure DrawButtonImage(ACanvas: TCanvas; ImageBounds: TRect);
|
|
procedure DrawButtonText(ACanvas: TCanvas; TextBounds: TRect);
|
|
|
|
procedure Redraw(const AKind: TJvRedrawKind);
|
|
procedure CalcDefaultButtonRect(Wnd: THandle);
|
|
|
|
{Paint related messages}
|
|
procedure HandleNCActivate(var Msg: TWMNCActivate);
|
|
procedure HandleNCPaintAfter(Wnd: THandle; var Msg: TWMNCPaint);
|
|
procedure HandleNCPaintBefore(Wnd: THandle; var Msg: TWMNCPaint);
|
|
{Mouse down-related messages}
|
|
function HandleButtonDown(var Msg: TWMNCHitMessage): Boolean;
|
|
function HandleButtonUp(var Msg: TWMNCHitMessage): Boolean;
|
|
function HandleHitTest(var Msg: TWMNCHitTest): Boolean;
|
|
function HandleMouseMove(var Msg: TWMNCHitMessage): Boolean;
|
|
procedure HandleNCMouseMove(var Msg: TWMNCHitMessage);
|
|
{Other}
|
|
function HandleNotify(var Msg: TWMNotify): Boolean;
|
|
|
|
procedure ImageListChange(Sender: TObject);
|
|
procedure DoActionChange(Sender: TObject);
|
|
|
|
function MouseOnButton(X, Y: Integer; const TranslateToScreenCoord: Boolean): Boolean;
|
|
procedure SetParentShowHint(const Value: Boolean);
|
|
procedure SetShowHint(const Value: Boolean);
|
|
protected
|
|
procedure ActionChange(Sender: TObject; CheckDefaults: Boolean); dynamic;
|
|
procedure CalcButtonParts(ACanvas: TCanvas; ButtonRect: TRect; var RectText, RectImage: TRect);
|
|
function GetActionLinkClass: TJvCaptionButtonActionLinkClass; dynamic;
|
|
procedure Loaded; override;
|
|
procedure MouseDown(Button: TMouseButton; Shift: TShiftState; X, Y: Integer); virtual;
|
|
procedure MouseUp(Button: TMouseButton; Shift: TShiftState; X, Y: Integer); virtual;
|
|
procedure MouseMove(Shift: TShiftState; X, Y: Integer); virtual;
|
|
procedure Notification(AComponent: TComponent; Operation: TOperation); override;
|
|
procedure UpdateButtonRect(Wnd: THandle);
|
|
|
|
property ActionLink: TJvCaptionButtonActionLink read FActionLink write FActionLink;
|
|
property IsImageVisible: Boolean read GetIsImageVisible;
|
|
{$IFDEF JVCLThemesEnabled}
|
|
// The value of IsThemed stays the same until a WM_THEMECHANGED is received.
|
|
property IsThemed: Boolean read GetIsThemed;
|
|
{$ENDIF JVCLThemesEnabled}
|
|
property MouseInControl: Boolean read FMouseInControl;
|
|
property ParentFormHandle: THandle read GetParentFormHandle;
|
|
property ParentForm: TCustomForm read GetParentForm;
|
|
public
|
|
constructor Create(AOwner: TComponent); override;
|
|
destructor Destroy; override;
|
|
procedure Assign(Source: TPersistent); override;
|
|
procedure InitiateAction; virtual;
|
|
procedure ResetButton;
|
|
procedure Click; dynamic;
|
|
|
|
property DefaultButtonWidth: Integer read FDefaultButtonWidth;
|
|
published
|
|
property Action: TBasicAction read GetAction write SetAction;
|
|
property Alignment: TAlignment read FAlignment write SetAlignment default taLeftJustify;
|
|
property ButtonHeight: Integer read FHeight write SetHeight default 0;
|
|
property ButtonLeft: Integer read FLeft write SetLeft default 0;
|
|
property ButtonTop: Integer read FTop write SetTop default 0;
|
|
property ButtonWidth: Integer read FWidth write SetWidth default 0;
|
|
property Caption: string read FCaption write SetCaption stored IsCaptionStored;
|
|
property Down: Boolean read FDown write SetDown default False;
|
|
{$IFDEF JVCLThemesEnabled}
|
|
property ForceDrawSimple: Boolean read FForceDrawSimple write SetForceDrawSimple default False;
|
|
{$ENDIF JVCLThemesEnabled}
|
|
property ShowHint: Boolean read FShowHint write SetShowHint default False;
|
|
property ParentShowHint: Boolean read FParentShowHint write SetParentShowHint default True;
|
|
property Enabled: Boolean read FEnabled write SetEnabled stored IsEnabledStored default True;
|
|
property Font: TFont read FFont write SetFont;
|
|
property Hint: string read FHint write FHint stored IsHintStored;
|
|
property ImageIndex: TImageIndex read FImageIndex write SetImageIndex stored IsImageIndexStored default -1;
|
|
property Images: TCustomImageList read FImages write SetImages;
|
|
property Layout: TJvCaptionButtonLayout read FLayout write SetLayout default cbImageLeft;
|
|
property Margin: Integer read FMargin write SetMargin default -1;
|
|
property Position: Integer read FPosition write SetPosition default 0;
|
|
property Spacing: Integer read FSpacing write SetSpacing default 4;
|
|
property Standard: TJvStandardButton read FStandard write SetStandard default tsbNone;
|
|
property Toggle: Boolean read FToggle write SetToggle default False;
|
|
property Visible: Boolean read FVisible write SetVisible default True;
|
|
property OnClick: TNotifyEvent read FOnClick write FOnClick;
|
|
property OnMouseUp: TMouseEvent read FOnMouseUp write FOnMouseUp;
|
|
property OnMouseDown: TMouseEvent read FOnMouseDown write FOnMouseDown;
|
|
property OnMouseMove: TMouseMoveEvent read FOnMouseMove write FOnMouseMove;
|
|
end;
|
|
|
|
function TransparentBlt(hdcDest: HDC; nXOriginDest, nYOriginDest, nWidthDest, hHeightDest: Integer;
|
|
hdcSrc: HDC; nXOriginSrc, nYOriginSrc, nWidthSrc, nHeightSrc: Integer;
|
|
crTransparent: UINT): BOOL; stdcall;
|
|
function AlphaBlend(hdcDest: HDC; nXOriginDest, nYOriginDest, nWidthDest,
|
|
nHeightDest: Integer; hdcSrc: HDC; nXOriginSrc, nYOriginSrc, nWidthSrc,
|
|
nHeightSrc: Integer; BlendFunction: BLENDFUNCTION): BOOL; stdcall;
|
|
|
|
{$IFDEF UNITVERSIONING}
|
|
const
|
|
UnitVersioning: TUnitVersionInfo = (
|
|
RCSfile: '$URL: https://jvcl.svn.sourceforge.net/svnroot/jvcl/tags/JVCL3_39/run/JvCaptionButton.pas $';
|
|
Revision: '$Revision: 12579 $';
|
|
Date: '$Date: 2009-10-26 20:59:53 +0100 (lun., 26 oct. 2009) $';
|
|
LogPath: 'JVCL\run'
|
|
);
|
|
{$ENDIF UNITVERSIONING}
|
|
|
|
implementation
|
|
|
|
uses
|
|
CommCtrl, Buttons, SysUtils,
|
|
JvThemes,
|
|
{$IFDEF JVCLThemesEnabled}
|
|
UxTheme,
|
|
{$IFNDEF COMPILER7_UP}
|
|
TmSchema,
|
|
{$ENDIF !COMPILER7_UP}
|
|
{$ENDIF JVCLThemesEnabled}
|
|
JvDsgnIntf, JvConsts, JvJCLUtils, JvResources, JvWndProcHook, JvJVCLUtils;
|
|
|
|
const
|
|
{ Msimg32.dll is included in Windows 98 and later }
|
|
Msimg32DLLName = 'Msimg32.dll';
|
|
|
|
TransparentBltName = 'TransparentBlt';
|
|
AlphaBlendName = 'AlphaBlend';
|
|
|
|
htCaptionButton = HTSIZELAST + 1;
|
|
Alignments: array [TAlignment] of Word = (DT_LEFT, DT_RIGHT, DT_CENTER);
|
|
|
|
var
|
|
GMsimg32Handle: THandle = 0;
|
|
GTriedLoadMsimg32Dll: Boolean = False;
|
|
|
|
_AlphaBlend: Pointer;
|
|
_TransparentBlt: Pointer;
|
|
|
|
{$IFDEF JVCLThemesEnabled}
|
|
|
|
type
|
|
{ (rb) I couldn't get the alpha channel to work with the normal TBitmap so
|
|
introduced TAlphaBitmap. TBitmapAdapter hides the implementation details
|
|
of the TBitmap/TAlphaBitmap }
|
|
|
|
TAlphaBitmap = class(TObject)
|
|
private
|
|
FHandle: HDC;
|
|
FBitmapInfo: TBitmapInfo;
|
|
FDIBHandle: HBitmap;
|
|
FOldBitmap: HBitmap;
|
|
FBitsMem: Pointer;
|
|
FBitCount: Byte;
|
|
FHasAlphaChannel: Boolean;
|
|
function GetWidth: Integer;
|
|
function GetHeight: Integer;
|
|
protected
|
|
procedure CreateHandle(AWidth, AHeight: Integer);
|
|
function CreateDIB(ADC: HDC; AWidth, AHeight: Integer): HBitmap;
|
|
procedure Duplicate(Src: HBitmap);
|
|
procedure FreeHandle;
|
|
procedure InitAlpha;
|
|
public
|
|
destructor Destroy; override;
|
|
procedure Assign(Source: TPersistent);
|
|
|
|
procedure LoadFromResourceID(Instance: THandle; ResID: Integer);
|
|
procedure LoadFromResourceName(Instance: THandle; const ResName: string);
|
|
|
|
property Handle: HDC read FHandle;
|
|
property Width: Integer read GetWidth;
|
|
property Height: Integer read GetHeight;
|
|
property Data: Pointer read FBitsMem;
|
|
property BitCount: Byte read FBitCount;
|
|
property HasAlphaChannel: Boolean read FHasAlphaChannel;
|
|
end;
|
|
|
|
TBitmapAdapter = class(TObject)
|
|
private
|
|
FBitmap: TObject;
|
|
FMargins: TMargins;
|
|
FTransparentColor: TColorRef;
|
|
function GetHeight: Integer;
|
|
function GetWidth: Integer;
|
|
function GetIsValid: Boolean;
|
|
public
|
|
constructor Create; virtual;
|
|
destructor Destroy; override;
|
|
procedure Clear;
|
|
procedure Assign(Source: TPersistent);
|
|
|
|
procedure LoadFromResourceName(Instance: THandle; const ResName: string);
|
|
procedure LoadFromResourceID(Instance: THandle; ResID: Integer);
|
|
|
|
function Draw(ACanvas: TCanvas; const Rect: TRect; AMargins: PMargins): Boolean;
|
|
function DrawFixed(ACanvas: TCanvas; const X, Y: Integer): Boolean;
|
|
function DrawFixedPart(ACanvas: TCanvas; const DestRect: TRect; const SrcX, SrcY: Integer): Boolean;
|
|
function DrawPart(ACanvas: TCanvas; const SrcRect, DestRect: TRect; AMargins: PMargins): Boolean;
|
|
|
|
property Margins: TMargins read FMargins write FMargins;
|
|
property Width: Integer read GetWidth;
|
|
property Height: Integer read GetHeight;
|
|
property IsValid: Boolean read GetIsValid;
|
|
property TransparentColor: TColorRef read FTransparentColor write FTransparentColor;
|
|
end;
|
|
|
|
TGlobalXPData = class(TObject)
|
|
private
|
|
FCaptionButtonHeight: Integer;
|
|
FCaptionButtonCount: Integer;
|
|
FCaptionButtons: TBitmapAdapter;
|
|
|
|
FIsThemed: Boolean;
|
|
FBitmapValid: Boolean;
|
|
FClientCount: Integer;
|
|
public
|
|
constructor Create; virtual;
|
|
destructor Destroy; override;
|
|
|
|
procedure AddClient;
|
|
procedure RemoveClient;
|
|
|
|
procedure Update;
|
|
procedure DrawSimple(ACanvas: TCanvas; State: Integer; const DrawRect: TRect);
|
|
function Draw(ACanvas: TCanvas; State: Integer; const DrawRect: TRect): Boolean;
|
|
property IsThemed: Boolean read FIsThemed;
|
|
end;
|
|
|
|
var
|
|
GGlobalXPData: TGlobalXPData;
|
|
|
|
//=== Local procedures =======================================================
|
|
|
|
function IsVistaOrNewer: Boolean;
|
|
begin
|
|
Result := (Win32Platform = VER_PLATFORM_WIN32_NT) and
|
|
(Win32MajorVersion >= 6);
|
|
end;
|
|
|
|
function GlobalXPData: TGlobalXPData;
|
|
begin
|
|
if not Assigned(GGlobalXPData) then
|
|
GGlobalXPData := TGlobalXPData.Create;
|
|
|
|
Result := GGlobalXPData;
|
|
end;
|
|
|
|
function TranslateBitmapFileName(const S: string): string;
|
|
var
|
|
I: Integer;
|
|
begin
|
|
Result := S;
|
|
for I := 1 to Length(S) do
|
|
case S[I] of
|
|
'A'..'Z', '0'..'9':
|
|
{do nothing};
|
|
'a'..'z':
|
|
Result[I] := UpCase(S[I]);
|
|
else
|
|
Result[I] := '_';
|
|
end;
|
|
end;
|
|
|
|
procedure DupBits(Src, Dst: HBitmap; Size: TPoint);
|
|
var
|
|
MemDC: HDC;
|
|
DesktopDC: HDC;
|
|
OldBitmap: HBitmap;
|
|
begin
|
|
OldBitmap := 0;
|
|
DesktopDC := GetDC(GetDesktopWindow);
|
|
MemDC := CreateCompatibleDC(DesktopDC);
|
|
try
|
|
OldBitmap := SelectObject(MemDC, Src);
|
|
BitBlt(Dst, 0, 0, Size.X, Size.Y, MemDC, 0, 0, SRCCOPY);
|
|
finally
|
|
SelectObject(MemDC, OldBitmap);
|
|
ReleaseDC(GetDesktopWindow, DesktopDC);
|
|
DeleteDC(MemDC);
|
|
end;
|
|
end;
|
|
|
|
function GetHasAlphaChannel(Data: PRGBQuad; Count: Integer): Boolean;
|
|
begin
|
|
Result := False;
|
|
|
|
while Count > 0 do
|
|
begin
|
|
Result := Data.rgbReserved <> 0;
|
|
if Result then
|
|
Break;
|
|
Inc(Data);
|
|
Dec(Count);
|
|
end;
|
|
end;
|
|
|
|
procedure PreMultiplyAlphaChannel(Data: PRGBQuad; Count: Integer);
|
|
begin
|
|
while Count > 0 do
|
|
begin
|
|
with Data^ do
|
|
begin
|
|
rgbBlue := (rgbBlue * rgbReserved + 128) div 255;
|
|
rgbGreen := (rgbGreen * rgbReserved + 128) div 255;
|
|
rgbRed := (rgbRed * rgbReserved + 128) div 255;
|
|
end;
|
|
Inc(Data);
|
|
Dec(Count);
|
|
end;
|
|
end;
|
|
|
|
{$ENDIF JVCLThemesEnabled}
|
|
|
|
procedure UnloadMsimg32Dll;
|
|
begin
|
|
_TransparentBlt := nil;
|
|
_AlphaBlend := nil;
|
|
if GMsimg32Handle > 0 then
|
|
FreeLibrary(GMsimg32Handle);
|
|
GMsimg32Handle := 0;
|
|
end;
|
|
|
|
procedure LoadMsimg32Dll;
|
|
begin
|
|
GTriedLoadMsimg32Dll := True;
|
|
GMsimg32Handle := SafeLoadLibrary(Msimg32DLLName);
|
|
if GMsimg32Handle <> 0 then
|
|
begin
|
|
_TransparentBlt := GetProcAddress(GMsimg32Handle, TransparentBltName);
|
|
_AlphaBlend := GetProcAddress(GMsimg32Handle, AlphaBlendName);
|
|
end;
|
|
end;
|
|
|
|
{$IFDEF JVCLThemesEnabled}
|
|
|
|
function TransparentBltStretch(DestDC: HDC; const DestRect: TRect;
|
|
SourceDC: HDC; const SourceRect: TRect; const SizingMargins: TMargins;
|
|
const TransparentColor: TColor): Boolean;
|
|
var
|
|
ESourceWidth, ESourceHeight: Integer;
|
|
EDestWidth, EDestHeight: Integer;
|
|
LastOriginSource: TPoint;
|
|
LastOriginDest: TPoint;
|
|
begin
|
|
{ Source Dest
|
|
|
|
|--------------| |--------------------|
|
|
| A | B | C | | A | B | C |
|
|
|-- |------|---| |-- |------------|---|
|
|
| | | | | | | |
|
|
| D | E | F | | | | |
|
|
| | | | => | D | E | F |
|
|
|---|------|---| | | | |
|
|
| G | H | I | | | | |
|
|
|--------------| |--------------------|
|
|
| G | H | I |
|
|
|-- |------------|---|
|
|
}
|
|
ESourceWidth := SourceRect.Right - SourceRect.Left - SizingMargins.cxLeftWidth - SizingMargins.cxRightWidth;
|
|
ESourceHeight := SourceRect.Bottom - SourceRect.Top - SizingMargins.cyTopHeight - SizingMargins.cyBottomHeight;
|
|
EDestWidth := DestRect.Right - DestRect.Left - SizingMargins.cxLeftWidth - SizingMargins.cxRightWidth;
|
|
EDestHeight := DestRect.Bottom - DestRect.Top - SizingMargins.cyTopHeight - SizingMargins.cyBottomHeight;
|
|
|
|
GetWindowOrgEx(SourceDC, LastOriginSource);
|
|
SetWindowOrgEx(SourceDC, LastOriginSource.X - SourceRect.Left, LastOriginSource.Y - SourceRect.Top, nil);
|
|
GetWindowOrgEx(DestDC, LastOriginDest);
|
|
SetWindowOrgEx(DestDC, LastOriginDest.X - DestRect.Left, LastOriginDest.Y - DestRect.Top, nil);
|
|
|
|
Result :=
|
|
{ A }
|
|
TransparentBlt(
|
|
DestDC,
|
|
0, 0, SizingMargins.cxLeftWidth, SizingMargins.cyTopHeight,
|
|
SourceDC,
|
|
0, 0, SizingMargins.cxLeftWidth, SizingMargins.cyTopHeight,
|
|
TransparentColor
|
|
) and
|
|
|
|
{ B }
|
|
TransparentBlt(
|
|
DestDC,
|
|
SizingMargins.cxLeftWidth, 0, EDestWidth, SizingMargins.cyTopHeight,
|
|
SourceDC,
|
|
SizingMargins.cxLeftWidth, 0, ESourceWidth, SizingMargins.cyTopHeight,
|
|
TransparentColor
|
|
) and
|
|
|
|
{ C }
|
|
TransparentBlt(
|
|
DestDC,
|
|
EDestWidth + SizingMargins.cxLeftWidth, 0, SizingMargins.cxRightWidth, SizingMargins.cyTopHeight,
|
|
SourceDC,
|
|
ESourceWidth + SizingMargins.cxLeftWidth, 0, SizingMargins.cxRightWidth, SizingMargins.cyTopHeight,
|
|
TransparentColor
|
|
) and
|
|
|
|
{ D }
|
|
TransparentBlt(
|
|
DestDC,
|
|
0, SizingMargins.cyTopHeight, SizingMargins.cxLeftWidth, EDestHeight,
|
|
SourceDC,
|
|
0, SizingMargins.cyTopHeight, SizingMargins.cxLeftWidth, ESourceHeight,
|
|
TransparentColor
|
|
) and
|
|
|
|
{ E }
|
|
TransparentBlt(
|
|
DestDC,
|
|
SizingMargins.cxLeftWidth, SizingMargins.cyTopHeight, EDestWidth, EDestHeight,
|
|
SourceDC,
|
|
SizingMargins.cxLeftWidth, SizingMargins.cyTopHeight, ESourceWidth, ESourceHeight,
|
|
TransparentColor
|
|
) and
|
|
|
|
{ F }
|
|
TransparentBlt(
|
|
DestDC,
|
|
EDestWidth + SizingMargins.cxLeftWidth, SizingMargins.cyTopHeight, SizingMargins.cxRightWidth, EDestHeight,
|
|
SourceDC,
|
|
ESourceWidth + SizingMargins.cxLeftWidth, SizingMargins.cyTopHeight, SizingMargins.cxRightWidth, ESourceHeight,
|
|
TransparentColor
|
|
) and
|
|
|
|
{ G }
|
|
TransparentBlt(
|
|
DestDC,
|
|
0, EDestHeight + SizingMargins.cyTopHeight, SizingMargins.cxLeftWidth, SizingMargins.cyBottomHeight,
|
|
SourceDC,
|
|
0, ESourceHeight + SizingMargins.cyTopHeight, SizingMargins.cxLeftWidth, SizingMargins.cyBottomHeight,
|
|
TransparentColor
|
|
) and
|
|
|
|
{ H }
|
|
TransparentBlt(
|
|
DestDC,
|
|
SizingMargins.cxLeftWidth, EDestHeight + SizingMargins.cyTopHeight, EDestWidth, SizingMargins.cyBottomHeight,
|
|
SourceDC,
|
|
SizingMargins.cxLeftWidth, ESourceHeight + SizingMargins.cyTopHeight, ESourceWidth, SizingMargins.cyBottomHeight,
|
|
TransparentColor
|
|
) and
|
|
|
|
{ I }
|
|
TransparentBlt(
|
|
DestDC,
|
|
EDestWidth + SizingMargins.cxLeftWidth, EDestHeight + SizingMargins.cyTopHeight,
|
|
SizingMargins.cxRightWidth, SizingMargins.cyBottomHeight,
|
|
SourceDC,
|
|
ESourceWidth + SizingMargins.cxLeftWidth, ESourceHeight + SizingMargins.cyTopHeight,
|
|
SizingMargins.cxRightWidth, SizingMargins.cyBottomHeight,
|
|
TransparentColor
|
|
);
|
|
|
|
SetWindowOrgEx(SourceDC, LastOriginSource.X, LastOriginSource.Y, nil);
|
|
SetWindowOrgEx(DestDC, LastOriginDest.X, LastOriginDest.Y, nil);
|
|
end;
|
|
|
|
function GetXPCaptionButtonBitmap(ABitmap: TBitmapAdapter; out BitmapCount: Integer): Boolean;
|
|
var
|
|
Handle: THandle;
|
|
ThemeFileNameW, BitmapFileNameW: array [0..MAX_PATH] of WideChar;
|
|
Details: TThemedElementDetails;
|
|
Margins: TMargins;
|
|
begin
|
|
ThemeFileNameW[MAX_PATH] := #0;
|
|
BitmapFileNameW[MAX_PATH] := #0;
|
|
|
|
Result := UxTheme.GetCurrentThemeName(ThemeFileNameW, MAX_PATH, nil, 0, nil, 0) = S_OK;
|
|
if not Result then
|
|
Exit;
|
|
|
|
Details := ThemeServices.GetElementDetails(twMinButtonNormal);
|
|
with Details do
|
|
Result := GetThemeFilename(ThemeServices.Theme[Element], Part, State,
|
|
TMT_IMAGEFILE, BitmapFileNameW, MAX_PATH) = S_OK;
|
|
if not Result then
|
|
Exit;
|
|
|
|
with Details do
|
|
Result := GetThemeInt(ThemeServices.Theme[Element], Part, State,
|
|
TMT_IMAGECOUNT, BitmapCount) = S_OK;
|
|
if not Result then
|
|
Exit;
|
|
|
|
Result := BitmapCount > 0;
|
|
if not Result then
|
|
Exit;
|
|
|
|
with Details do
|
|
if GetThemeMargins(ThemeServices.Theme[Element], 0, Part, State,
|
|
TMT_SIZINGMARGINS, nil, Margins) <> S_OK then
|
|
FillChar(Margins, SizeOf(Margins), 0);
|
|
ABitmap.Margins := Margins;
|
|
|
|
Handle := SafeLoadLibrary(ThemeFileNameW, SEM_NOOPENFILEERRORBOX or SEM_FAILCRITICALERRORS);
|
|
if Handle <> 0 then
|
|
try
|
|
ABitmap.Assign(nil); // fixes GDI resource leak
|
|
ABitmap.LoadFromResourceName(Handle, TranslateBitmapFileName(BitmapFileNameW));
|
|
{ (rb) can't determine actual transparent color? }
|
|
ABitmap.TransparentColor := clFuchsia;
|
|
|
|
Result := (ABitmap.Width > 0) and (ABitmap.Height > 0);
|
|
finally
|
|
FreeLibrary(Handle);
|
|
end;
|
|
end;
|
|
|
|
{$ENDIF JVCLThemesEnabled}
|
|
|
|
//=== Global procedures ======================================================
|
|
|
|
function AlphaBlend;
|
|
begin
|
|
if not GTriedLoadMsimg32Dll then
|
|
LoadMsimg32Dll;
|
|
Result := Assigned(_AlphaBlend);
|
|
if Result then
|
|
asm
|
|
mov esp, ebp
|
|
pop ebp
|
|
jmp [_AlphaBlend]
|
|
end;
|
|
end;
|
|
|
|
function TransparentBlt;
|
|
begin
|
|
if not GTriedLoadMsimg32Dll then
|
|
LoadMsimg32Dll;
|
|
Result := Assigned(_TransparentBlt);
|
|
if Result then
|
|
asm
|
|
mov esp, ebp
|
|
pop ebp
|
|
jmp [_TransparentBlt]
|
|
end;
|
|
end;
|
|
|
|
{$IFDEF JVCLThemesEnabled}
|
|
|
|
//=== { TAlphaBitmap } =======================================================
|
|
|
|
destructor TAlphaBitmap.Destroy;
|
|
begin
|
|
FreeHandle;
|
|
inherited Destroy;
|
|
end;
|
|
|
|
procedure TAlphaBitmap.Assign(Source: TPersistent);
|
|
begin
|
|
// What to do here when Source is not nil???
|
|
if not Assigned(Source) then
|
|
FreeHandle
|
|
else
|
|
;
|
|
end;
|
|
|
|
function TAlphaBitmap.CreateDIB(ADC: HDC; AWidth, AHeight: Integer): HBitmap;
|
|
begin
|
|
with FBitmapInfo.bmiHeader do
|
|
begin
|
|
biSize := SizeOf(FBitmapInfo.bmiHeader);
|
|
biWidth := AWidth;
|
|
biHeight := AHeight;
|
|
biPlanes := 1;
|
|
biBitCount := 32;
|
|
biCompression := BI_RGB;
|
|
biSizeImage := AWidth * AHeight * 4;
|
|
end;
|
|
// Create the DIB
|
|
Result := CreateDIBSection(ADC, FBitmapInfo, DIB_RGB_COLORS, FBitsMem, 0, 0);
|
|
end;
|
|
|
|
procedure TAlphaBitmap.CreateHandle(AWidth, AHeight: Integer);
|
|
var
|
|
H: HBitmap;
|
|
begin
|
|
FreeHandle;
|
|
|
|
H := CreateScreenCompatibleDC;
|
|
FDIBHandle := CreateDIB(H, AWidth, AHeight);
|
|
if FDIBHandle <> 0 then
|
|
FOldBitmap := SelectObject(H, FDIBHandle)
|
|
else
|
|
FOldBitmap := 0;
|
|
FHandle := H;
|
|
end;
|
|
|
|
procedure TAlphaBitmap.Duplicate(Src: HBitmap);
|
|
var
|
|
Bitmap: Windows.TBitmap;
|
|
begin
|
|
GetObject(Src, SizeOf(Bitmap), @Bitmap);
|
|
CreateHandle(Bitmap.bmWidth, Bitmap.bmHeight);
|
|
|
|
DupBits(Src, FHandle, Point(Bitmap.bmWidth, Bitmap.bmHeight));
|
|
end;
|
|
|
|
procedure TAlphaBitmap.FreeHandle;
|
|
begin
|
|
if FHandle <> 0 then
|
|
begin
|
|
if FDIBHandle <> 0 then
|
|
begin
|
|
if FOldBitmap <> 0 then
|
|
SelectObject(FHandle, FOldBitmap);
|
|
DeleteObject(FDIBHandle);
|
|
end;
|
|
DeleteDC(FHandle);
|
|
end;
|
|
end;
|
|
|
|
function TAlphaBitmap.GetHeight: Integer;
|
|
begin
|
|
Result := FBitmapInfo.bmiHeader.biHeight;
|
|
end;
|
|
|
|
function TAlphaBitmap.GetWidth: Integer;
|
|
begin
|
|
Result := FBitmapInfo.bmiHeader.biWidth;
|
|
end;
|
|
|
|
procedure TAlphaBitmap.InitAlpha;
|
|
var
|
|
Count: Integer;
|
|
begin
|
|
Count := Width * Height;
|
|
if BitCount < 32 then
|
|
FHasAlphaChannel := False
|
|
else
|
|
begin
|
|
FHasAlphaChannel := GetHasAlphaChannel(Data, Count);
|
|
|
|
if HasAlphaChannel then
|
|
PreMultiplyAlphaChannel(Data, Count);
|
|
end;
|
|
end;
|
|
|
|
procedure TAlphaBitmap.LoadFromResourceID(Instance: THandle; ResID: Integer);
|
|
var
|
|
Stream: TCustomMemoryStream;
|
|
BitmapInfoHeader: TBitmapInfoHeader;
|
|
BitmapHandle: HBitmap;
|
|
begin
|
|
Stream := TResourceStream.CreateFromID(Instance, ResID, RT_BITMAP);
|
|
try
|
|
Stream.Read(BitmapInfoHeader, SizeOf(TBitmapInfoHeader));
|
|
FBitCount := BitmapInfoHeader.biBitCount;
|
|
finally
|
|
Stream.Free;
|
|
end;
|
|
|
|
if FBitCount = 32 then
|
|
begin
|
|
BitmapHandle := LoadImage(Instance, PChar(ResID), IMAGE_BITMAP, 0, 0, LR_CREATEDIBSECTION);
|
|
if BitmapHandle = 0 then
|
|
Exit;
|
|
|
|
Duplicate(BitmapHandle);
|
|
DeleteObject(BitmapHandle);
|
|
|
|
InitAlpha;
|
|
end;
|
|
end;
|
|
|
|
procedure TAlphaBitmap.LoadFromResourceName(Instance: THandle; const ResName: string);
|
|
var
|
|
Stream: TCustomMemoryStream;
|
|
BitmapInfoHeader: TBitmapInfoHeader;
|
|
BitmapHandle: HBitmap;
|
|
begin
|
|
Stream := TResourceStream.Create(Instance, ResName, RT_BITMAP);
|
|
try
|
|
Stream.Read(BitmapInfoHeader, SizeOf(TBitmapInfoHeader));
|
|
FBitCount := BitmapInfoHeader.biBitCount;
|
|
finally
|
|
Stream.Free;
|
|
end;
|
|
|
|
if FBitCount = 32 then
|
|
begin
|
|
BitmapHandle := LoadImage(Instance, PChar(ResName), IMAGE_BITMAP, 0, 0, LR_CREATEDIBSECTION);
|
|
if BitmapHandle = 0 then
|
|
Exit;
|
|
|
|
Duplicate(BitmapHandle);
|
|
DeleteObject(BitmapHandle);
|
|
|
|
InitAlpha;
|
|
end;
|
|
end;
|
|
|
|
//=== { TBitmapAdapter } =====================================================
|
|
|
|
constructor TBitmapAdapter.Create;
|
|
begin
|
|
inherited Create;
|
|
FTransparentColor := clFuchsia;
|
|
end;
|
|
|
|
destructor TBitmapAdapter.Destroy;
|
|
begin
|
|
FBitmap.Free;
|
|
inherited Destroy;
|
|
end;
|
|
|
|
procedure TBitmapAdapter.Assign(Source: TPersistent);
|
|
begin
|
|
if FBitmap is TBitmap then
|
|
(FBitmap as TBitmap).Assign(Source)
|
|
else
|
|
if FBitmap is TAlphaBitmap then
|
|
(FBitmap as TAlphaBitmap).Assign(Source);
|
|
end;
|
|
|
|
procedure TBitmapAdapter.Clear;
|
|
begin
|
|
FreeAndNil(FBitmap);
|
|
end;
|
|
|
|
function TBitmapAdapter.Draw(ACanvas: TCanvas; const Rect: TRect;
|
|
AMargins: PMargins): Boolean;
|
|
begin
|
|
if (Rect.Right - Rect.Left = Width) and (Rect.Bottom - Rect.Top = Height) then
|
|
Result := DrawFixedPart(ACanvas, Rect, 0, 0)
|
|
else
|
|
begin
|
|
if AMargins = nil then
|
|
AMargins := @FMargins;
|
|
|
|
if FBitmap is TAlphaBitmap then
|
|
with TAlphaBitmap(FBitmap) do
|
|
Result := TransparentBltStretch(ACanvas.Handle, Rect, Handle,
|
|
Bounds(0, 0, Width, Height), AMargins^, FTransparentColor)
|
|
else
|
|
if FBitmap is TBitmap then
|
|
with TBitmap(FBitmap) do
|
|
Result := TransparentBltStretch(ACanvas.Handle, Rect, Canvas.Handle,
|
|
Bounds(0, 0, Width, Height), AMargins^, FTransparentColor)
|
|
else
|
|
Result := False;
|
|
end;
|
|
end;
|
|
|
|
function TBitmapAdapter.DrawFixed(ACanvas: TCanvas; const X, Y: Integer): Boolean;
|
|
begin
|
|
Result := DrawFixedPart(ACanvas, Bounds(X, Y, Width, Height), 0, 0);
|
|
end;
|
|
|
|
function TBitmapAdapter.DrawFixedPart(ACanvas: TCanvas;
|
|
const DestRect: TRect; const SrcX, SrcY: Integer): Boolean;
|
|
var
|
|
BlendFunction: TBlendFunction;
|
|
W, H: Integer;
|
|
begin
|
|
W := DestRect.Right - DestRect.Left;
|
|
H := DestRect.Bottom - DestRect.Top;
|
|
|
|
if FBitmap is TAlphaBitmap then
|
|
begin
|
|
with TAlphaBitmap(FBitmap) do
|
|
begin
|
|
BlendFunction.BlendOp := AC_SRC_OVER;
|
|
BlendFunction.BlendFlags := 0;
|
|
BlendFunction.SourceConstantAlpha := $FF;
|
|
BlendFunction.AlphaFormat := AC_SRC_ALPHA;
|
|
|
|
Result := AlphaBlend(ACanvas.Handle, DestRect.Left, DestRect.Top, W, H,
|
|
Handle, SrcX, SrcY, W, H, BlendFunction);
|
|
end;
|
|
end
|
|
else
|
|
if FBitmap is TBitmap then
|
|
with TBitmap(FBitmap) do
|
|
Result := TransparentBlt(ACanvas.Handle, DestRect.Left, DestRect.Top, W, H,
|
|
Canvas.Handle, SrcX, SrcY, W, H, Self.TransparentColor)
|
|
else
|
|
Result := False;
|
|
end;
|
|
|
|
function TBitmapAdapter.DrawPart(ACanvas: TCanvas; const SrcRect,
|
|
DestRect: TRect; AMargins: PMargins): Boolean;
|
|
begin
|
|
// Same width/height?
|
|
if (SrcRect.Right - SrcRect.Left = DestRect.Right - DestRect.Left) and
|
|
(SrcRect.Bottom - SrcRect.Top = DestRect.Bottom - DestRect.Top) then
|
|
Result := DrawFixedPart(ACanvas, DestRect, SrcRect.Left, SrcRect.Top)
|
|
else
|
|
begin
|
|
if AMargins = nil then
|
|
AMargins := @FMargins;
|
|
|
|
if FBitmap is TAlphaBitmap then
|
|
with TAlphaBitmap(FBitmap) do
|
|
Result := TransparentBltStretch(ACanvas.Handle, DestRect, Handle, SrcRect,
|
|
AMargins^, Self.TransparentColor)
|
|
else
|
|
if FBitmap is TBitmap then
|
|
with TBitmap(FBitmap) do
|
|
Result := TransparentBltStretch(ACanvas.Handle, DestRect, Canvas.Handle, SrcRect,
|
|
AMargins^, Self.TransparentColor)
|
|
else
|
|
Result := False;
|
|
end;
|
|
end;
|
|
|
|
function TBitmapAdapter.GetHeight: Integer;
|
|
begin
|
|
if FBitmap is TAlphaBitmap then
|
|
Result := TAlphaBitmap(FBitmap).Height
|
|
else
|
|
if FBitmap is TBitmap then
|
|
Result := TBitmap(FBitmap).Height
|
|
else
|
|
Result := 0;
|
|
end;
|
|
|
|
function TBitmapAdapter.GetIsValid: Boolean;
|
|
begin
|
|
Result := Assigned(FBitmap);
|
|
end;
|
|
|
|
function TBitmapAdapter.GetWidth: Integer;
|
|
begin
|
|
if FBitmap is TAlphaBitmap then
|
|
Result := TAlphaBitmap(FBitmap).Width
|
|
else
|
|
if FBitmap is TBitmap then
|
|
Result := TBitmap(FBitmap).Width
|
|
else
|
|
Result := 0;
|
|
end;
|
|
|
|
procedure TBitmapAdapter.LoadFromResourceID(Instance: THandle; ResID: Integer);
|
|
var
|
|
AlphaBitmap: TAlphaBitmap;
|
|
begin
|
|
Clear;
|
|
|
|
AlphaBitmap := TAlphaBitmap.Create;
|
|
try
|
|
AlphaBitmap.LoadFromResourceID(Instance, ResID);
|
|
if AlphaBitmap.BitCount < 32 then
|
|
begin
|
|
FBitmap := TBitmap.Create;
|
|
TBitmap(FBitmap).LoadFromResourceID(Instance, ResID);
|
|
end
|
|
else
|
|
begin
|
|
FBitmap := AlphaBitmap;
|
|
AlphaBitmap := nil;
|
|
end;
|
|
finally
|
|
AlphaBitmap.Free;
|
|
end;
|
|
end;
|
|
|
|
procedure TBitmapAdapter.LoadFromResourceName(Instance: THandle; const ResName: string);
|
|
var
|
|
AlphaBitmap: TAlphaBitmap;
|
|
begin
|
|
Clear;
|
|
|
|
AlphaBitmap := TAlphaBitmap.Create;
|
|
try
|
|
AlphaBitmap.LoadFromResourceName(Instance, ResName);
|
|
if AlphaBitmap.BitCount < 32 then
|
|
begin
|
|
FBitmap := TBitmap.Create;
|
|
TBitmap(FBitmap).LoadFromResourceName(Instance, ResName);
|
|
end
|
|
else
|
|
begin
|
|
FBitmap := AlphaBitmap;
|
|
AlphaBitmap := nil;
|
|
end;
|
|
finally
|
|
AlphaBitmap.Free;
|
|
end;
|
|
end;
|
|
|
|
//=== { TGlobalXPData } ======================================================
|
|
|
|
constructor TGlobalXPData.Create;
|
|
begin
|
|
inherited Create;
|
|
Update;
|
|
end;
|
|
|
|
destructor TGlobalXPData.Destroy;
|
|
begin
|
|
FCaptionButtons.Free;
|
|
inherited Destroy;
|
|
end;
|
|
|
|
procedure TGlobalXPData.AddClient;
|
|
begin
|
|
Inc(FClientCount);
|
|
end;
|
|
|
|
function TGlobalXPData.Draw(ACanvas: TCanvas; State: Integer;
|
|
const DrawRect: TRect): Boolean;
|
|
var
|
|
SrcRect: TRect;
|
|
begin
|
|
Result := FBitmapValid;
|
|
if not Result then
|
|
Exit;
|
|
|
|
{ State is 1-based }
|
|
if (State >= FCaptionButtonCount) and (State > 4) then
|
|
State := ((State - 1) mod 4) + 1;
|
|
if State > FCaptionButtonCount then
|
|
State := FCaptionButtonCount;
|
|
|
|
SrcRect := Bounds(0, FCaptionButtonHeight * (State - 1),
|
|
FCaptionButtons.Width, FCaptionButtonHeight);
|
|
|
|
Result := FCaptionButtons.DrawPart(ACanvas, SrcRect, DrawRect, nil);
|
|
end;
|
|
|
|
procedure TGlobalXPData.DrawSimple(ACanvas: TCanvas; State: Integer;
|
|
const DrawRect: TRect);
|
|
const
|
|
// Normal, Hot, Pushed, Disabled,
|
|
cCaptionButton: array [0..3] of TThemedWindow =
|
|
(twMinButtonNormal, twMinButtonHot, twMinButtonPushed, twMinButtonDisabled);
|
|
cNormalButton: array [0..3] of TThemedButton =
|
|
(tbPushButtonNormal, tbPushButtonHot, tbPushButtonPressed, tbPushButtonDisabled);
|
|
var
|
|
Details: TThemedElementDetails;
|
|
DrawRgn: HRGN;
|
|
begin
|
|
{ Draw the button in 2 pieces, draw the edge of a caption button, and the
|
|
inner of a normal button, because drawing a normal button looks ugly }
|
|
|
|
// State = 1..8 -> State = 0..3
|
|
State := (State - 1) mod 4;
|
|
|
|
{ 1a. Draw the outer bit as a caption button }
|
|
Details := ThemeServices.GetElementDetails(cCaptionButton[State]);
|
|
ThemeServices.DrawElement(ACanvas.Handle, Details, DrawRect);
|
|
|
|
{ 1b. Draw the inner bit as a normal button }
|
|
with DrawRect do
|
|
DrawRgn := CreateRectRgn(Left + 1, Top + 1, Right - 1, Bottom - 1);
|
|
try
|
|
Details := ThemeServices.GetElementDetails(cNormalButton[State]);
|
|
SelectClipRgn(ACanvas.Handle, DrawRgn);
|
|
ThemeServices.DrawElement(ACanvas.Handle, Details, DrawRect);
|
|
SelectClipRgn(ACanvas.Handle, 0);
|
|
finally
|
|
DeleteObject(DrawRgn);
|
|
end;
|
|
end;
|
|
|
|
procedure TGlobalXPData.RemoveClient;
|
|
begin
|
|
Dec(FClientCount);
|
|
if FClientCount = 0 then
|
|
begin
|
|
if Self = GGlobalXPData then
|
|
GGlobalXPData := nil;
|
|
Self.Free;
|
|
end;
|
|
end;
|
|
|
|
procedure TGlobalXPData.Update;
|
|
begin
|
|
FIsThemed := ThemeServices.ThemesAvailable and IsThemeActive and IsAppThemed;
|
|
if not FIsThemed then
|
|
Exit;
|
|
|
|
if FCaptionButtons = nil then
|
|
FCaptionButtons := TBitmapAdapter.Create;
|
|
|
|
FBitmapValid := GetXPCaptionButtonBitmap(FCaptionButtons, FCaptionButtonCount);
|
|
if FBitmapValid then
|
|
FCaptionButtonHeight := FCaptionButtons.Height div FCaptionButtonCount
|
|
else
|
|
FreeAndNil(FCaptionButtons);
|
|
end;
|
|
|
|
{$ENDIF JVCLThemesEnabled}
|
|
|
|
//=== { TJvCaptionButton } ===================================================
|
|
|
|
constructor TJvCaptionButton.Create(AOwner: TComponent);
|
|
begin
|
|
if not (AOwner is TCustomForm) then
|
|
raise EJVCLException.CreateRes(@RsEOwnerMustBeTCustomForm);
|
|
|
|
inherited Create(AOwner);
|
|
|
|
{ Defaults }
|
|
FAlignment := taLeftJustify;
|
|
FHeight := 0;
|
|
FLeft := 0;
|
|
FTop := 0;
|
|
FWidth := 0;
|
|
FEnabled := True;
|
|
FImageIndex := -1;
|
|
FLayout := cbImageLeft;
|
|
FMargin := -1;
|
|
FPosition := 0;
|
|
FSpacing := 4;
|
|
FStandard := tsbNone;
|
|
FToggle := False;
|
|
FVisible := True;
|
|
|
|
FNeedRecalculate := True;
|
|
FCaption := '';
|
|
FDown := False;
|
|
FToolTipHandle := 0;
|
|
|
|
FFont := TFont.Create;
|
|
FBuffer := TBitmap.Create;
|
|
|
|
FImageChangeLink := TChangeLink.Create;
|
|
FImageChangeLink.OnChange := ImageListChange;
|
|
FParentShowHint := True;
|
|
|
|
FCurrentWindowState := TCustomForm(AOwner).WindowState;
|
|
|
|
{$IFDEF JVCLThemesEnabled}
|
|
GlobalXPData.AddClient;
|
|
{$ENDIF JVCLThemesEnabled}
|
|
|
|
{$IFDEF JVCLThemesEnabled}
|
|
if IsVistaOrNewer and IsThemed then // Windows Vista
|
|
FForceRedraw := True;
|
|
{$ENDIF JVCLThemesEnabled}
|
|
|
|
Hook;
|
|
end;
|
|
|
|
destructor TJvCaptionButton.Destroy;
|
|
begin
|
|
DestroyToolTip;
|
|
|
|
UnHook;
|
|
Redraw(rkTotalCaptionBar);
|
|
|
|
FFont.Free;
|
|
FBuffer.Free;
|
|
|
|
FreeAndNil(FActionLink);
|
|
FreeAndNil(FImageChangeLink);
|
|
|
|
{$IFDEF JVCLThemesEnabled}
|
|
GlobalXPData.RemoveClient;
|
|
{$ENDIF JVCLThemesEnabled}
|
|
|
|
inherited Destroy;
|
|
end;
|
|
|
|
procedure TJvCaptionButton.ActionChange(Sender: TObject;
|
|
CheckDefaults: Boolean);
|
|
begin
|
|
if Sender is TCustomAction then
|
|
with TCustomAction(Sender) do
|
|
begin
|
|
if not CheckDefaults or not Assigned(Self.Images) then
|
|
Self.Images := ActionList.Images;
|
|
if not CheckDefaults or (Self.Caption = '') then
|
|
Self.Caption := Caption;
|
|
if not CheckDefaults or Self.Enabled then
|
|
Self.Enabled := Enabled;
|
|
if not CheckDefaults or (Self.Hint = '') then
|
|
Self.Hint := Hint;
|
|
if not CheckDefaults or (Self.ImageIndex = -1) then
|
|
Self.ImageIndex := ImageIndex;
|
|
if not CheckDefaults or Self.Visible then
|
|
Self.Visible := Visible;
|
|
if not CheckDefaults or not Assigned(Self.OnClick) then
|
|
Self.OnClick := OnExecute;
|
|
end;
|
|
end;
|
|
|
|
procedure TJvCaptionButton.Assign(Source: TPersistent);
|
|
begin
|
|
if Source is TJvCaptionButton then
|
|
begin
|
|
Alignment := TJvCaptionButton(Source).Alignment;
|
|
ButtonHeight := TJvCaptionButton(Source).ButtonHeight;
|
|
ButtonLeft := TJvCaptionButton(Source).ButtonLeft;
|
|
ButtonTop := TJvCaptionButton(Source).ButtonTop;
|
|
ButtonWidth := TJvCaptionButton(Source).ButtonWidth;
|
|
Caption := TJvCaptionButton(Source).Caption;
|
|
ShowHint := TJvCaptionButton(Source).ShowHint;
|
|
ParentShowHint := TJvCaptionButton(Source).ParentShowHint;
|
|
Enabled := TJvCaptionButton(Source).Enabled;
|
|
Font := TJvCaptionButton(Source).Font;
|
|
Hint := TJvCaptionButton(Source).Hint;
|
|
ImageIndex := TJvCaptionButton(Source).ImageIndex;
|
|
Images := TJvCaptionButton(Source).Images;
|
|
Layout := TJvCaptionButton(Source).Layout;
|
|
Margin := TJvCaptionButton(Source).Margin;
|
|
Position := TJvCaptionButton(Source).Position;
|
|
Spacing := TJvCaptionButton(Source).Spacing;
|
|
Standard := TJvCaptionButton(Source).Standard;
|
|
// set toggle before down
|
|
Toggle := TJvCaptionButton(Source).Toggle;
|
|
Down := TJvCaptionButton(Source).Down;
|
|
Visible := TJvCaptionButton(Source).Visible;
|
|
end
|
|
else
|
|
inherited Assign(Source);
|
|
end;
|
|
|
|
procedure TJvCaptionButton.CalcButtonParts(ACanvas: TCanvas;
|
|
ButtonRect: TRect; var RectText, RectImage: TRect);
|
|
// copied from TJvCustomImageButton
|
|
const
|
|
CDefaultMargin = 4;
|
|
var
|
|
BlockWidth, ButtonWidth, ButtonHeight, BlockMargin, InternalSpacing: Integer;
|
|
LMargin: Integer;
|
|
OldFont: TFont;
|
|
begin
|
|
SetRect(RectText, 0, 0, 0, 0);
|
|
OldFont := ACanvas.Font;
|
|
ACanvas.Font := Font;
|
|
DrawText(ACanvas, Caption, -1, RectText, DT_CALCRECT or Alignments[FAlignment]);
|
|
ACanvas.Font := OldFont;
|
|
if IsImageVisible then
|
|
begin
|
|
with Images do
|
|
SetRect(RectImage, 0, 0, Width - 1, Height - 1);
|
|
InternalSpacing := Spacing;
|
|
end
|
|
else
|
|
begin
|
|
SetRect(RectImage, 0, 0, 0, 0);
|
|
InternalSpacing := 0;
|
|
end;
|
|
BlockWidth := RectImage.Right + InternalSpacing + RectText.Right;
|
|
ButtonWidth := ButtonRect.Right - ButtonRect.Left;
|
|
if Margin = -1 then
|
|
LMargin := CDefaultMargin
|
|
else
|
|
LMargin := Margin;
|
|
|
|
case Alignment of
|
|
taLeftJustify:
|
|
BlockMargin := LMargin;
|
|
taRightJustify:
|
|
BlockMargin := ButtonWidth - BlockWidth - LMargin - 1;
|
|
else {taCenter}
|
|
BlockMargin := (ButtonWidth - BlockWidth) div 2
|
|
end;
|
|
case Layout of
|
|
cbImageLeft:
|
|
begin
|
|
OffsetRect(RectImage, BlockMargin, 0);
|
|
OffsetRect(RectText, RectImage.Right + InternalSpacing, 0);
|
|
end;
|
|
cbImageRight:
|
|
begin
|
|
OffsetRect(RectText, BlockMargin, 0);
|
|
OffsetRect(RectImage, RectText.Right + InternalSpacing, 0);
|
|
end;
|
|
end;
|
|
ButtonHeight := ButtonRect.Bottom - ButtonRect.Top;
|
|
OffsetRect(RectImage, ButtonRect.Left, (ButtonHeight - RectImage.Bottom) div 2 + ButtonRect.Top);
|
|
OffsetRect(RectText, ButtonRect.Left, (ButtonHeight - RectText.Bottom) div 2 + ButtonRect.Top);
|
|
end;
|
|
|
|
procedure TJvCaptionButton.CalcDefaultButtonRect(Wnd: THandle);
|
|
const
|
|
CSpaceBetweenButtons = 2;
|
|
var
|
|
Style: DWORD;
|
|
ExStyle: DWORD;
|
|
FrameSize: TSize;
|
|
Placement: WindowPlacement;
|
|
begin
|
|
if Wnd = 0 then
|
|
Exit;
|
|
|
|
{ 0. Init some local vars }
|
|
FNeedRecalculate := False;
|
|
Style := GetWindowLong(Wnd, GWL_STYLE);
|
|
FHasCaption := Style and WS_CAPTION = WS_CAPTION;
|
|
if not FHasCaption then
|
|
Exit;
|
|
|
|
Placement.length := SizeOf(WindowPlacement);
|
|
GetWindowPlacement(Wnd, @Placement);
|
|
ExStyle := GetWindowLong(Wnd, GWL_EXSTYLE);
|
|
FHasSmallCaption := ExStyle and WS_EX_TOOLWINDOW = WS_EX_TOOLWINDOW;
|
|
{$IFDEF JVCLThemesEnabled}
|
|
if not IsThemed and (Placement.showCmd = SW_SHOWMINIMIZED) then
|
|
FHasSmallCaption := False;
|
|
FCaptionActive := (GetActiveWindow = Wnd) and IsForegroundTask;
|
|
{$ELSE}
|
|
if Placement.showCmd = SW_SHOWMINIMIZED then
|
|
FHasSmallCaption := False;
|
|
{$ENDIF JVCLThemesEnabled}
|
|
|
|
if (Style and WS_THICKFRAME = WS_THICKFRAME) and (Placement.showCmd <> SW_SHOWMINIMIZED) then
|
|
begin
|
|
FrameSize.cx := GetSystemMetrics(SM_CXSIZEFRAME);
|
|
FrameSize.cy := GetSystemMetrics(SM_CYSIZEFRAME);
|
|
end
|
|
else
|
|
begin
|
|
FrameSize.cx := GetSystemMetrics(SM_CXFIXEDFRAME);
|
|
FrameSize.cy := GetSystemMetrics(SM_CYFIXEDFRAME);
|
|
end;
|
|
|
|
{ 1. Calc FDefaultButtonTop }
|
|
FDefaultButtonTop := FrameSize.cy + 2;
|
|
|
|
{ 2. Calc FDefaultButtonHeight }
|
|
if FHasSmallCaption then
|
|
FCaptionHeight := GetSystemMetrics(SM_CYSMCAPTION)
|
|
else
|
|
FCaptionHeight := GetSystemMetrics(SM_CYCAPTION);
|
|
FDefaultButtonHeight := FCaptionHeight - 5;
|
|
|
|
{ 3. Calc FDefaultButtonWidth }
|
|
{$IFDEF JVCLThemesEnabled}
|
|
if IsThemed then
|
|
begin
|
|
if IsVistaOrNewer then
|
|
begin
|
|
if FHasSmallCaption then
|
|
begin
|
|
FDefaultButtonWidth := GetSystemMetrics(SM_CXSMSIZE) - 4
|
|
end
|
|
else
|
|
begin
|
|
// This is not exactly correct but WM_GETTITLEBARINFOEX returns the coordinates
|
|
// for the "Glass" style. But because we paint into the NC area, out window uses
|
|
// the "Basic" style.
|
|
FDefaultButtonWidth := GetSystemMetrics(SM_CXSIZE) - 4;
|
|
end;
|
|
|
|
// Adjust position
|
|
FDefaultButtonTop := FDefaultButtonTop - 2;
|
|
FDefaultButtonHeight := FCaptionHeight - 3;
|
|
end
|
|
else
|
|
FDefaultButtonWidth := FDefaultButtonHeight;
|
|
end
|
|
else
|
|
{$ENDIF JVCLThemesEnabled}
|
|
if FHasSmallCaption then
|
|
FDefaultButtonWidth := GetSystemMetrics(SM_CXSMSIZE) - CSpaceBetweenButtons
|
|
else
|
|
FDefaultButtonWidth := GetSystemMetrics(SM_CXSIZE) - CSpaceBetweenButtons;
|
|
|
|
{ 4. Calc FDefaultButtonLeft }
|
|
FDefaultButtonLeft := FrameSize.cx;
|
|
Inc(FDefaultButtonLeft, FDefaultButtonWidth + CSpaceBetweenButtons);
|
|
|
|
if Style and WS_SYSMENU = WS_SYSMENU then
|
|
begin
|
|
{ 4a. Avoid close button }
|
|
Inc(FDefaultButtonLeft, FDefaultButtonWidth + CSpaceBetweenButtons);
|
|
|
|
if not FHasSmallCaption then
|
|
begin
|
|
if (Style and WS_MAXIMIZEBOX = WS_MAXIMIZEBOX) or
|
|
(Style and WS_MINIMIZEBOX = WS_MINIMIZEBOX) then
|
|
begin
|
|
{$IFDEF JVCLThemesEnabled}
|
|
if IsThemed then
|
|
{ 4b. If it have Max or Min button, both are visible. When themed
|
|
the CONTEXTHELP button is then never visible }
|
|
Inc(FDefaultButtonLeft, 2 * (FDefaultButtonWidth + CSpaceBetweenButtons))
|
|
else
|
|
{$ENDIF JVCLThemesEnabled}
|
|
begin
|
|
{ 4b. If it have Max or Min button, both are visible. }
|
|
Inc(FDefaultButtonLeft, 2 * FDefaultButtonWidth + CSpaceBetweenButtons);
|
|
|
|
{ 4c. If it have CONTEXTHELP button, avoid it. }
|
|
if ((Style and WS_MAXIMIZEBOX = 0) or (Style and WS_MINIMIZEBOX = 0)) and
|
|
(ExStyle and WS_EX_CONTEXTHELP = WS_EX_CONTEXTHELP) then
|
|
Inc(FDefaultButtonLeft, FDefaultButtonWidth + 2 * CSpaceBetweenButtons);
|
|
end;
|
|
end
|
|
else
|
|
{ 4c. If it have CONTEXTHELP button, avoid it. }
|
|
if ExStyle and WS_EX_CONTEXTHELP = WS_EX_CONTEXTHELP then
|
|
Inc(FDefaultButtonLeft, FDefaultButtonWidth + CSpaceBetweenButtons);
|
|
end;
|
|
end;
|
|
end;
|
|
|
|
procedure TJvCaptionButton.Click;
|
|
begin
|
|
if csDesigning in ComponentState then
|
|
DesignerSelectComponent(Self)
|
|
else
|
|
if Enabled then
|
|
begin
|
|
{ Call OnClick if assigned and not equal to associated action's OnExecute.
|
|
If associated action's OnExecute assigned then call it, otherwise, call
|
|
OnClick. }
|
|
if Assigned(FOnClick) and (Action <> nil) and (@FOnClick <> @Action.OnExecute) then
|
|
FOnClick(Self)
|
|
else
|
|
if {not (csDesigning in ComponentState) and} Assigned(ActionLink) then
|
|
FActionLink.Execute(Self)
|
|
else
|
|
if Assigned(FOnClick) then
|
|
FOnClick(Self);
|
|
end;
|
|
end;
|
|
|
|
procedure TJvCaptionButton.CreateToolTip(Wnd: THandle);
|
|
var
|
|
ToolInfo: TToolInfo;
|
|
begin
|
|
if csDesigning in ComponentState then
|
|
Exit;
|
|
|
|
DestroyToolTip;
|
|
|
|
if Wnd = 0 then
|
|
Exit;
|
|
|
|
FToolTipHandle := CreateWindowEx(0, TOOLTIPS_CLASS, nil, TTS_ALWAYSTIP,
|
|
Integer(CW_USEDEFAULT), Integer(CW_USEDEFAULT),
|
|
Integer(CW_USEDEFAULT), Integer(CW_USEDEFAULT),
|
|
ParentForm.Handle, // Thus automatically destroyed if ParentForm handle is destroyed.
|
|
0, HInstance, nil);
|
|
|
|
if FToolTipHandle = 0 then
|
|
Exit;
|
|
|
|
// initialize tooltip info
|
|
ToolInfo.cbSize := SizeOf(TToolInfo);
|
|
ToolInfo.uFlags := TTF_IDISHWND; { Thus ignores rect param }
|
|
ToolInfo.hwnd := Wnd;
|
|
ToolInfo.uId := Wnd;
|
|
ToolInfo.lpszText := LPSTR_TEXTCALLBACK;
|
|
|
|
// register button with tooltip
|
|
SendMessage(FToolTipHandle, TTM_ADDTOOL, 0, LPARAM(@ToolInfo));
|
|
end;
|
|
|
|
procedure TJvCaptionButton.DestroyToolTip;
|
|
begin
|
|
if FToolTipHandle <> 0 then
|
|
begin
|
|
DestroyWindow(FToolTipHandle);
|
|
FToolTipHandle := 0;
|
|
end;
|
|
end;
|
|
|
|
procedure TJvCaptionButton.DoActionChange(Sender: TObject);
|
|
begin
|
|
if Sender = Action then
|
|
ActionChange(Sender, False);
|
|
end;
|
|
|
|
procedure TJvCaptionButton.DrawButton(DC: HDC);
|
|
var
|
|
Canvas: TControlCanvas;
|
|
begin
|
|
if not Visible or not FHasCaption or (csDestroying in ComponentState) then
|
|
Exit;
|
|
|
|
Canvas := TControlCanvas.Create;
|
|
try
|
|
Canvas.Handle := DC;
|
|
|
|
UpdateButtonRect(ParentFormHandle);
|
|
|
|
with FButtonRect do
|
|
begin
|
|
FBuffer.Width := Right - Left;
|
|
FBuffer.Height := Bottom - Top;
|
|
end;
|
|
|
|
{$IFDEF JVCLThemesEnabled}
|
|
DrawButtonBackground(FBuffer.Canvas);
|
|
{$ENDIF JVCLThemesEnabled}
|
|
|
|
{ We do a buffered drawing, otherwise you get flickering on XP, and you
|
|
have to hassle with the clipping rects. }
|
|
if FStandard <> tsbNone then
|
|
DrawStandardButton(FBuffer.Canvas)
|
|
else
|
|
DrawNonStandardButton(FBuffer.Canvas);
|
|
|
|
Canvas.Draw(FButtonRect.Left, FButtonRect.Top, FBuffer);
|
|
finally
|
|
Canvas.Handle := 0;
|
|
Canvas.Free;
|
|
end;
|
|
end;
|
|
|
|
{$IFDEF JVCLThemesEnabled}
|
|
procedure TJvCaptionButton.DrawButtonBackground(ACanvas: TCanvas);
|
|
const
|
|
CCaption: array [Boolean, Boolean] of TThemedWindow =
|
|
(
|
|
(twCaptionInactive, twCaptionActive),
|
|
(twSmallCaptionInactive, twSmallCaptionActive)
|
|
);
|
|
var
|
|
Details: TThemedElementDetails;
|
|
ClipRect: TRect;
|
|
CaptionRect: TRect;
|
|
begin
|
|
if not IsThemed or (csDestroying in ComponentState) then
|
|
Exit;
|
|
|
|
{ We basically draw the background to display 4 pixels - the corners of the
|
|
rect - correct <g>. Don't know a better way to do this. }
|
|
|
|
{ Determine the rect of the caption }
|
|
GetWindowRect(ParentFormHandle, CaptionRect);
|
|
OffsetRect(CaptionRect, -CaptionRect.Left, -CaptionRect.Top);
|
|
CaptionRect.Bottom := FCaptionHeight + 4;
|
|
{ Offset it so the place where the button is, is at (0, 0) }
|
|
OffsetRect(CaptionRect, -FButtonRect.Left, -FButtonRect.Top);
|
|
with FButtonRect do
|
|
ClipRect := Rect(0, 0, Right - Left, Bottom - Top);
|
|
|
|
Details := ThemeServices.GetElementDetails(CCaption[FHasSmallCaption, FCaptionActive]);
|
|
ThemeServices.DrawElement(ACanvas.Handle, Details, CaptionRect, @ClipRect);
|
|
end;
|
|
{$ENDIF JVCLThemesEnabled}
|
|
|
|
procedure TJvCaptionButton.DrawButtonImage(ACanvas: TCanvas; ImageBounds: TRect);
|
|
begin
|
|
if csDestroying in ComponentState then
|
|
Exit;
|
|
with ImageBounds do
|
|
if IsImageVisible then
|
|
Images.Draw(ACanvas, Left, Top, ImageIndex, Enabled);
|
|
end;
|
|
|
|
procedure TJvCaptionButton.DrawButtonText(ACanvas: TCanvas; TextBounds: TRect);
|
|
var
|
|
Flags: DWORD;
|
|
OldFont: TFont;
|
|
begin
|
|
Flags := DT_VCENTER or Alignments[FAlignment];
|
|
with ACanvas do
|
|
begin
|
|
Brush.Style := bsClear;
|
|
if not Enabled then
|
|
begin
|
|
OffsetRect(TextBounds, 1, 1);
|
|
OldFont := Font;
|
|
Font := Self.Font;
|
|
Font.Color := clBtnHighlight;
|
|
DrawText(ACanvas, Caption, Length(Caption), TextBounds, Flags);
|
|
OffsetRect(TextBounds, -1, -1);
|
|
Font.Color := clBtnShadow;
|
|
DrawText(ACanvas, Caption, Length(Caption), TextBounds, Flags);
|
|
Font := OldFont;
|
|
end
|
|
else
|
|
begin
|
|
OldFont := Font;
|
|
Font := Self.Font;
|
|
DrawText(ACanvas, Caption, Length(Caption), TextBounds, Flags);
|
|
Font := OldFont;
|
|
end;
|
|
end;
|
|
end;
|
|
|
|
procedure TJvCaptionButton.DrawNonStandardButton(ACanvas: TCanvas);
|
|
{$IFDEF JVCLThemesEnabled}
|
|
const
|
|
cState_Normal = 1;
|
|
cState_Hot = 2;
|
|
cState_Pushed = 3;
|
|
cState_Disabled = 4;
|
|
{$ENDIF JVCLThemesEnabled}
|
|
var
|
|
DrawRect: TRect;
|
|
RectText, RectImage: TRect;
|
|
{$IFDEF JVCLThemesEnabled}
|
|
State: Integer;
|
|
DrawRgn: HRGN;
|
|
{$ENDIF JVCLThemesEnabled}
|
|
begin
|
|
if csDestroying in ComponentState then
|
|
Exit;
|
|
with FButtonRect do
|
|
DrawRect := Rect(0, 0, Right - Left, Bottom - Top);
|
|
|
|
{$IFDEF JVCLThemesEnabled}
|
|
// Satisfy the compiler
|
|
DrawRgn := 0;
|
|
|
|
{ 1. Draw the button }
|
|
if IsThemed then
|
|
begin
|
|
if not Enabled then
|
|
State := 4
|
|
else
|
|
if FDown then
|
|
State := 3
|
|
else
|
|
if FMouseInControl then
|
|
State := 2
|
|
else
|
|
State := 1;
|
|
|
|
{ Special state for buttons drawn on a not active caption }
|
|
if not FCaptionActive then
|
|
Inc(State, 4);
|
|
|
|
if ForceDrawSimple or not GlobalXPData.Draw(ACanvas, State, DrawRect) then
|
|
GlobalXPData.DrawSimple(ACanvas, State, DrawRect)
|
|
end
|
|
else
|
|
{$ENDIF JVCLThemesEnabled}
|
|
DrawButtonFace(ACanvas, DrawRect, 1, bsAutoDetect, False, FDown, False);
|
|
|
|
{ 2. Draw the text & picture }
|
|
{$IFDEF JVCLThemesEnabled}
|
|
if IsThemed then
|
|
begin
|
|
{ 2a. If themed, only draw in the inner bit of the button using a clip region }
|
|
with DrawRect do
|
|
DrawRgn := CreateRectRgn(Left + 2, Top + 2, Right - 2, Bottom - 2);
|
|
|
|
SelectClipRgn(ACanvas.Handle, DrawRgn);
|
|
end;
|
|
{$ENDIF JVCLThemesEnabled}
|
|
|
|
if FDown then
|
|
begin
|
|
{$IFDEF JVCLThemesEnabled}
|
|
if IsThemed then
|
|
OffsetRect(DrawRect, 1, 0)
|
|
else
|
|
{$ENDIF JVCLThemesEnabled}
|
|
OffsetRect(DrawRect, 1, 1);
|
|
end;
|
|
{ 2b. Calc position and Draw the picture & text }
|
|
CalcButtonParts(ACanvas, DrawRect, RectText, RectImage);
|
|
DrawButtonText(ACanvas, RectText);
|
|
DrawButtonImage(ACanvas, RectImage);
|
|
{ 2c. Clean up }
|
|
{$IFDEF JVCLThemesEnabled}
|
|
if IsThemed then
|
|
begin
|
|
SelectClipRgn(ACanvas.Handle, 0);
|
|
DeleteObject(DrawRgn);
|
|
end;
|
|
{$ENDIF JVCLThemesEnabled}
|
|
end;
|
|
|
|
procedure TJvCaptionButton.DrawStandardButton(ACanvas: TCanvas);
|
|
const
|
|
{$IFDEF JVCLThemesEnabled}
|
|
CElements: array [TJvStandardButton] of TThemedWindow =
|
|
(twWindowDontCare, twCloseButtonNormal, twHelpButtonNormal, twMaxButtonNormal,
|
|
twMinButtonNormal, twRestoreButtonNormal, twMinButtonNormal);
|
|
{$ENDIF JVCLThemesEnabled}
|
|
CDrawFlags: array [TJvStandardButton] of Word =
|
|
(0, DFCS_CAPTIONCLOSE, DFCS_CAPTIONHELP, DFCS_CAPTIONMAX, DFCS_CAPTIONMIN,
|
|
DFCS_CAPTIONRESTORE, 0);
|
|
CDown: array [Boolean] of Word = (0, DFCS_PUSHED);
|
|
CEnabled: array [Boolean] of Word = (DFCS_INACTIVE, 0);
|
|
var
|
|
DrawRect: TRect;
|
|
{$IFDEF JVCLThemesEnabled}
|
|
Details: TThemedElementDetails;
|
|
CaptionButton: TThemedWindow;
|
|
{$ENDIF JVCLThemesEnabled}
|
|
begin
|
|
if csDestroying in ComponentState then
|
|
Exit;
|
|
with FButtonRect do
|
|
DrawRect := Rect(0, 0, Right - Left, Bottom - Top);
|
|
|
|
{$IFDEF JVCLThemesEnabled}
|
|
if IsThemed then
|
|
begin
|
|
CaptionButton := CElements[FStandard];
|
|
{ Note : There is only a small close button (??) }
|
|
if FHasSmallCaption and (FStandard = tsbClose) then
|
|
CaptionButton := twSmallCloseButtonNormal;
|
|
|
|
if not Enabled then
|
|
Inc(CaptionButton, 3)
|
|
else
|
|
if FDown then
|
|
{ If Down and inactive, draw inactive border }
|
|
Inc(CaptionButton, 2)
|
|
else
|
|
if FMouseInControl then
|
|
Inc(CaptionButton);
|
|
|
|
Details := ThemeServices.GetElementDetails(CaptionButton);
|
|
{ Special state for buttons drawn on a not active caption }
|
|
if not FCaptionActive and (Details.State = 1) then
|
|
Details.State := 5;
|
|
ThemeServices.DrawElement(ACanvas.Handle, Details, DrawRect)
|
|
end
|
|
else
|
|
{$ENDIF JVCLThemesEnabled}
|
|
if Standard = tsbMinimizeToTray then
|
|
begin
|
|
DrawButtonFace(ACanvas, DrawRect, 1, bsAutoDetect, False, FDown, False);
|
|
if Enabled then
|
|
begin
|
|
ACanvas.Brush.Color := clWindowText;
|
|
with DrawRect do
|
|
ACanvas.FillRect(Rect(Right - 7, Bottom - 5, Right - 4, Bottom - 3));
|
|
end
|
|
else
|
|
begin
|
|
ACanvas.Brush.Color := clBtnHighlight;
|
|
with DrawRect do
|
|
ACanvas.FillRect(Rect(Right - 6, Bottom - 4, Right - 3, Bottom - 2));
|
|
ACanvas.Brush.Color := clBtnShadow;
|
|
with DrawRect do
|
|
ACanvas.FillRect(Rect(Right - 7, Bottom - 5, Right - 4, Bottom - 3));
|
|
end;
|
|
end
|
|
else
|
|
DrawFrameControl(ACanvas.Handle, DrawRect, DFC_CAPTION, {DFCS_ADJUSTRECT or}
|
|
CDrawFlags[Standard] or CDown[Down] or CEnabled[Enabled]);
|
|
end;
|
|
|
|
procedure TJvCaptionButton.ForwardToToolTip(Msg: TMessage);
|
|
var
|
|
ForwardMsg: TMsg;
|
|
begin
|
|
if FToolTipHandle = 0 then
|
|
Exit;
|
|
|
|
// forward to tool tip
|
|
ForwardMsg.lParam := Msg.LParam;
|
|
ForwardMsg.wParam := Msg.WParam;
|
|
ForwardMsg.message := Msg.Msg;
|
|
ForwardMsg.hwnd := ParentFormHandle;
|
|
SendMessage(FToolTipHandle, TTM_RELAYEVENT, 0, LPARAM(@ForwardMsg));
|
|
end;
|
|
|
|
function TJvCaptionButton.GetAction: TBasicAction;
|
|
begin
|
|
if FActionLink <> nil then
|
|
Result := FActionLink.Action
|
|
else
|
|
Result := nil;
|
|
end;
|
|
|
|
function TJvCaptionButton.GetActionLinkClass: TJvCaptionButtonActionLinkClass;
|
|
begin
|
|
Result := TJvCaptionButtonActionLink;
|
|
end;
|
|
|
|
function TJvCaptionButton.GetIsImageVisible: Boolean;
|
|
begin
|
|
Result := Assigned(Images) and (ImageIndex > -1) and (ImageIndex < Images.Count);
|
|
end;
|
|
|
|
{$IFDEF JVCLThemesEnabled}
|
|
function TJvCaptionButton.GetIsThemed: Boolean;
|
|
begin
|
|
Result := GlobalXPData.IsThemed;
|
|
end;
|
|
{$ENDIF JVCLThemesEnabled}
|
|
|
|
function TJvCaptionButton.GetParentForm: TCustomForm;
|
|
begin
|
|
if Owner is TControl then
|
|
Result := Forms.GetParentForm(TControl(Owner))
|
|
else
|
|
Result := nil;
|
|
end;
|
|
|
|
function TJvCaptionButton.GetParentFormHandle: THandle;
|
|
var
|
|
P: TCustomForm;
|
|
begin
|
|
P := GetParentForm;
|
|
if Assigned(P) and P.HandleAllocated then
|
|
Result := P.Handle
|
|
else
|
|
Result := 0;
|
|
end;
|
|
|
|
function TJvCaptionButton.HandleButtonDown(var Msg: TWMNCHitMessage): Boolean;
|
|
begin
|
|
Result := Visible and Enabled and (Msg.HitTest = htCaptionButton) and
|
|
MouseOnButton(Msg.XCursor, Msg.YCursor, False);
|
|
|
|
if Result then
|
|
begin
|
|
FMouseButtonDown := True;
|
|
if Toggle then
|
|
FDown := not FDown
|
|
else
|
|
FDown := True;
|
|
with TWMMouse(Msg) do
|
|
MouseDown(mbLeft, KeysToShiftState(Keys), XPos, YPos);
|
|
{if not Toggle then}
|
|
SetCapture(ParentFormHandle);
|
|
Redraw(rkIndirect);
|
|
|
|
{ Note: If Toggle = False -> click event is fired in HandleButtonUp }
|
|
if Toggle then
|
|
Click;
|
|
end
|
|
else
|
|
if FDown and not Toggle then
|
|
begin
|
|
FMouseButtonDown := False;
|
|
FDown := False;
|
|
Redraw(rkIndirect);
|
|
end;
|
|
end;
|
|
|
|
function TJvCaptionButton.HandleButtonUp(var Msg: TWMNCHitMessage): Boolean;
|
|
var
|
|
DoClick: Boolean;
|
|
P: TPoint;
|
|
begin
|
|
Result := False;
|
|
|
|
if not FMouseButtonDown then
|
|
Exit;
|
|
|
|
Result := FDown and MouseOnButton(Msg.XCursor, Msg.YCursor, Msg.Msg = WM_LBUTTONUP);
|
|
{ Note: If Toggle = True -> click event is fired in HandleButtonDown }
|
|
DoClick := Result and not Toggle;
|
|
|
|
FMouseButtonDown := False;
|
|
ReleaseCapture;
|
|
|
|
if not Toggle then
|
|
begin
|
|
FDown := False;
|
|
Redraw(rkIndirect);
|
|
end;
|
|
|
|
if DoClick then
|
|
Click;
|
|
|
|
//(p3) we need to convert MouseUp message because they are in client coordinates (MouseDown are already in screen coords, so no need to change)
|
|
with TWMMouse(Msg) do
|
|
begin
|
|
P := Point(XPos, YPos);
|
|
Assert(ParentForm <> nil, '');
|
|
P := ParentForm.ClientToScreen(P);
|
|
MouseUp(mbLeft, KeysToShiftState(Keys), P.X, P.Y);
|
|
end;
|
|
end;
|
|
|
|
function TJvCaptionButton.HandleHitTest(var Msg: TWMNCHitTest): Boolean;
|
|
var
|
|
CurPos: TPoint;
|
|
begin
|
|
Result := Visible and MouseOnButton(Msg.XPos, Msg.YPos, False);
|
|
if Result then
|
|
Msg.Result := htCaptionButton;
|
|
|
|
if not Result and Visible and MouseInControl then
|
|
begin
|
|
// We can get weird hittest values (probably from the hint window) so
|
|
// double check that the mouse is not on the button.
|
|
// Actually we wrongfully assumed that Msg represents the current mouse
|
|
// position so we have to double check.
|
|
GetCursorPos(CurPos);
|
|
if not MouseOnButton(CurPos.X, CurPos.Y, False) then
|
|
begin
|
|
SetMouseInControl(False);
|
|
Redraw(rkIndirect);
|
|
end;
|
|
end;
|
|
|
|
//Result := False;
|
|
end;
|
|
|
|
function TJvCaptionButton.HandleMouseMove(var Msg: TWMNCHitMessage): Boolean;
|
|
var
|
|
DoRedraw: Boolean;
|
|
MouseWasInControl: Boolean;
|
|
begin
|
|
Result := FMouseButtonDown;
|
|
|
|
if Result then
|
|
begin
|
|
MouseWasInControl := FMouseInControl;
|
|
SetMouseInControl(MouseOnButton(Msg.XCursor, Msg.YCursor, Msg.Msg = WM_MOUSEMOVE));
|
|
DoRedraw := (FMouseInControl <> MouseWasInControl) or
|
|
// User presses mouse button, but left the caption button
|
|
(FDown and not Toggle and not FMouseInControl) or
|
|
// User presses mouse button, and enters the caption button
|
|
(not FDown and not Toggle and FMouseInControl);
|
|
|
|
FDown := (FDown and Toggle) or
|
|
(FMouseButtonDown and not Toggle and FMouseInControl);
|
|
if DoRedraw then
|
|
Redraw(rkIndirect);
|
|
end;
|
|
// (p3) don't handle mouse move here: it is triggered even if the mouse is outside the button
|
|
// with TWmMouseMove(Msg) do
|
|
// MouseMove(KeysToShiftState(Keys), XPos, YPos);
|
|
end;
|
|
|
|
procedure TJvCaptionButton.HandleNCActivate(var Msg: TWMNCActivate);
|
|
begin
|
|
{$IFDEF JVCLThemesEnabled}
|
|
FCaptionActive := Msg.Active;
|
|
{$ENDIF JVCLThemesEnabled}
|
|
SetMouseInControl(MouseInControl and Msg.Active);
|
|
|
|
Redraw(rkDirect);
|
|
end;
|
|
|
|
procedure TJvCaptionButton.HandleNCMouseMove(var Msg: TWMNCHitMessage);
|
|
var
|
|
IsOnButton: Boolean;
|
|
begin
|
|
IsOnButton := MouseOnButton(Msg.XCursor, Msg.YCursor, False);
|
|
if Visible then
|
|
begin
|
|
if (IsOnButton <> FMouseInControl) then
|
|
begin
|
|
SetMouseInControl(not FMouseInControl);
|
|
if not Down then
|
|
Redraw(rkIndirect);
|
|
end;
|
|
// (p3) only handle mouse move if we are inside the button or it will be triggered for the entire NC area
|
|
if IsOnButton then
|
|
with TWMMouseMove(Msg) do
|
|
MouseMove(KeysToShiftState(Keys), XPos, YPos);
|
|
end;
|
|
end;
|
|
|
|
procedure TJvCaptionButton.HandleNCPaintAfter(Wnd: THandle; var Msg: TWMNCPaint);
|
|
begin
|
|
if FRgnChanged then
|
|
begin
|
|
DeleteObject(Msg.RGN);
|
|
Msg.RGN := FSaveRgn;
|
|
FRgnChanged := False;
|
|
end;
|
|
|
|
Redraw(rkDirect);
|
|
end;
|
|
|
|
procedure TJvCaptionButton.HandleNCPaintBefore(Wnd: THandle; var Msg: TWMNCPaint);
|
|
var
|
|
WindowRect: TRect;
|
|
DrawRgn: HRGN;
|
|
LButtonRect: TRect;
|
|
begin
|
|
{ Note: There is one problem with this reduce flickering method: This
|
|
function is executed before windows handles the WM_NCPAINT and
|
|
HandleNCPaintAfter is executed after windows handles WM_NCPAINT.
|
|
|
|
When you resize a form, the value returned by API GetWindowRect is
|
|
adjusted when windows handles the WM_NCPAINT.
|
|
|
|
Thus return value of GetWindowRect in HandleNCPaintBefore differs
|
|
from return value of GetWindowRect in HandleNCPaintAfter.
|
|
->
|
|
Thus value of FButtonRect in HandleNCPaintBefore differs
|
|
from return value of FButtonRect in HandleNCPaintAfter.
|
|
|
|
(Diff is typically 1 pixel)
|
|
|
|
This causes a light flickering at the edge of the button when
|
|
you resize the form.
|
|
|
|
To see it, put Sleep(1000) or so, before and after the DrawButton call
|
|
in HandleNCPaintAfter and resize the screen horizontally
|
|
}
|
|
if Wnd = 0 then
|
|
Exit;
|
|
|
|
FSaveRgn := Msg.RGN;
|
|
FRgnChanged := False;
|
|
{ Calculate button rect in screen coordinates, put it in LButtonRect }
|
|
UpdateButtonRect(Wnd);
|
|
LButtonRect := FButtonRect;
|
|
GetWindowRect(Wnd, WindowRect);
|
|
OffsetRect(LButtonRect, WindowRect.Left, WindowRect.Top);
|
|
{ Check if button rect is in the to be updated region.. }
|
|
if RectInRegion(FSaveRgn, LButtonRect)
|
|
{$IFDEF JVCLThemesEnabled}
|
|
or FForceRedraw
|
|
{$ENDIF JVCLThemesEnabled}
|
|
then
|
|
begin
|
|
{$IFDEF JVCLThemesEnabled}
|
|
FForceRedraw := False;
|
|
{$ENDIF JVCLThemesEnabled}
|
|
{ ..If so remove the button rectangle from the region (otherwise the caption
|
|
background would be drawn over the button, which causes flicker) }
|
|
with LButtonRect do
|
|
DrawRgn := CreateRectRgn(Left, Top, Right, Bottom);
|
|
try
|
|
Msg.RGN := CreateRectRgn(0, 0, 1, 1);
|
|
FRgnChanged := True;
|
|
CombineRgn(Msg.RGN, FSaveRgn, DrawRgn, RGN_DIFF);
|
|
finally
|
|
DeleteObject(DrawRgn);
|
|
end;
|
|
end;
|
|
end;
|
|
|
|
function TJvCaptionButton.HandleNotify(var Msg: TWMNotify): Boolean;
|
|
var
|
|
CurPos: TPoint;
|
|
LButtonRect, WindowRect: TRect;
|
|
begin
|
|
// if we receive a TTN_GETDISPINFO notification
|
|
// and it is from the tooltip
|
|
Result := (Msg.NMHdr.code = TTN_NEEDTEXT) and (Msg.NMHdr.hwndFrom = FToolTipHandle);
|
|
|
|
if Result and (ShowHint or (ParentShowHint and ParentForm.ShowHint)) then
|
|
begin
|
|
// get cursor position
|
|
GetCursorPos(CurPos);
|
|
GetWindowRect(ParentFormHandle, WindowRect);
|
|
LButtonRect := FButtonRect;
|
|
OffsetRect(LButtonRect, WindowRect.Left, WindowRect.Top);
|
|
|
|
// if the mouse is in the area of the button
|
|
if PtInRect(LButtonRect, CurPos) then
|
|
{$IFDEF SUPPORTS_UNICODE}
|
|
if Msg.NMHdr.code = TTN_NEEDTEXTW then
|
|
begin
|
|
with PNMTTDispInfoW(Msg.NMHdr)^ do
|
|
begin
|
|
// then we return the hint
|
|
lpszText := PChar(FHint); // we do loose text here, but unicode should have kicked in anyway
|
|
hinst := 0;
|
|
uFlags := TTF_IDISHWND;
|
|
hdr.idFrom := ParentFormHandle;
|
|
end;
|
|
end
|
|
else
|
|
{$ENDIF SUPPORTS_UNICODE}
|
|
if Msg.NMHdr.code = TTN_NEEDTEXTA then
|
|
begin
|
|
with PNMTTDispInfoA(Msg.NMHdr)^ do
|
|
begin
|
|
// then we return the hint
|
|
lpszText := PAnsiChar(AnsiString(FHint)); // we do loose text here, but unicode should have kicked in anyway
|
|
hinst := 0;
|
|
uFlags := TTF_IDISHWND;
|
|
hdr.idFrom := ParentFormHandle;
|
|
end;
|
|
end
|
|
else
|
|
with PNMTTDispInfoW(Msg.NMHdr)^ do
|
|
begin
|
|
// then we return the hint
|
|
lpszText := PWideChar(WideString(FHint));
|
|
hinst := 0;
|
|
uFlags := TTF_IDISHWND;
|
|
hdr.idFrom := ParentFormHandle;
|
|
end
|
|
else
|
|
//else we hide the tooltip
|
|
HideToolTip;
|
|
end;
|
|
end;
|
|
|
|
procedure TJvCaptionButton.HideToolTip;
|
|
begin
|
|
if FToolTipHandle <> 0 then
|
|
SendMessage(FToolTipHandle, TTM_POP, 0, 0);
|
|
end;
|
|
|
|
procedure TJvCaptionButton.Hook;
|
|
var
|
|
P: TCustomForm;
|
|
begin
|
|
//if not Visible or not FHasCaption then
|
|
// Exit;
|
|
|
|
P := ParentForm;
|
|
if Assigned(P) then
|
|
begin
|
|
RegisterWndProcHook(P, WndProcAfter, hoAfterMsg);
|
|
RegisterWndProcHook(P, WndProcBefore, hoBeforeMsg);
|
|
|
|
if P.HandleAllocated then
|
|
CreateToolTip(P.Handle);
|
|
end;
|
|
end;
|
|
|
|
procedure TJvCaptionButton.ImageListChange(Sender: TObject);
|
|
begin
|
|
if Sender = Images then
|
|
Redraw(rkIndirect);
|
|
end;
|
|
|
|
procedure TJvCaptionButton.InitiateAction;
|
|
begin
|
|
if FActionLink <> nil then
|
|
FActionLink.Update;
|
|
end;
|
|
|
|
function TJvCaptionButton.IsCaptionStored: Boolean;
|
|
begin
|
|
Result := (ActionLink = nil) or not FActionLink.IsCaptionLinked;
|
|
end;
|
|
|
|
function TJvCaptionButton.IsEnabledStored: Boolean;
|
|
begin
|
|
Result := (ActionLink = nil) or not FActionLink.IsEnabledLinked;
|
|
end;
|
|
|
|
function TJvCaptionButton.IsHintStored: Boolean;
|
|
begin
|
|
Result := (ActionLink = nil) or not FActionLink.IsHintLinked;
|
|
end;
|
|
|
|
function TJvCaptionButton.IsImageIndexStored: Boolean;
|
|
begin
|
|
Result := (ActionLink = nil) or not FActionLink.IsImageIndexLinked;
|
|
end;
|
|
|
|
procedure TJvCaptionButton.Loaded;
|
|
begin
|
|
inherited Loaded;
|
|
|
|
CreateToolTip(ParentFormHandle);
|
|
Redraw(rkTotalCaptionBar);
|
|
end;
|
|
|
|
procedure TJvCaptionButton.MouseDown(Button: TMouseButton; Shift: TShiftState; X, Y: Integer);
|
|
begin
|
|
if Assigned(FOnMouseDown) then
|
|
FOnMouseDown(Self, Button, Shift, X, Y);
|
|
end;
|
|
|
|
procedure TJvCaptionButton.MouseMove(Shift: TShiftState; X, Y: Integer);
|
|
begin
|
|
if Assigned(FOnMouseMove) then
|
|
FOnMouseMove(Self, Shift, X, Y);
|
|
end;
|
|
|
|
function TJvCaptionButton.MouseOnButton(X, Y: Integer;
|
|
const TranslateToScreenCoord: Boolean): Boolean;
|
|
var
|
|
WindowRect: TRect;
|
|
Wnd: THandle;
|
|
P: TPoint;
|
|
begin
|
|
Wnd := ParentFormHandle;
|
|
Result := Wnd <> 0;
|
|
if not Result then
|
|
Exit;
|
|
|
|
P := Point(X, Y);
|
|
if TranslateToScreenCoord then
|
|
Windows.ClientToScreen(Wnd, P);
|
|
|
|
GetWindowRect(Wnd, WindowRect);
|
|
Result := PtInRect(FClickRect, Point(P.X - WindowRect.Left, P.Y - WindowRect.Top));
|
|
end;
|
|
|
|
procedure TJvCaptionButton.MouseUp(Button: TMouseButton; Shift: TShiftState; X, Y: Integer);
|
|
begin
|
|
if Assigned(FOnMouseUp) then
|
|
FOnMouseUp(Self, Button, Shift, X, Y);
|
|
end;
|
|
|
|
procedure TJvCaptionButton.Notification(AComponent: TComponent;
|
|
Operation: TOperation);
|
|
begin
|
|
inherited Notification(AComponent, Operation);
|
|
if (AComponent = Images) and (Operation = opRemove) then
|
|
Images := nil;
|
|
end;
|
|
|
|
procedure TJvCaptionButton.Redraw(const AKind: TJvRedrawKind);
|
|
var
|
|
Wnd: THandle;
|
|
DC: HDC;
|
|
begin
|
|
if csLoading in ComponentState then
|
|
Exit;
|
|
|
|
Wnd := ParentFormHandle;
|
|
if Wnd = 0 then
|
|
Exit;
|
|
|
|
case AKind of
|
|
rkDirect:
|
|
begin
|
|
DC := GetWindowDC(Wnd);
|
|
try
|
|
DrawButton(DC);
|
|
finally
|
|
ReleaseDC(Wnd, DC);
|
|
end;
|
|
end;
|
|
rkIndirect:
|
|
begin
|
|
UpdateButtonRect(Wnd);
|
|
RedrawWindow(Wnd, @FButtonRect, 0, RDW_FRAME or RDW_INVALIDATE or RDW_ERASE);
|
|
end;
|
|
rkTotalCaptionBar:
|
|
begin
|
|
UpdateButtonRect(Wnd);
|
|
RedrawWindow(Wnd, PRect(0), 0, RDW_FRAME or RDW_NOINTERNALPAINT or RDW_INVALIDATE);
|
|
end;
|
|
end;
|
|
end;
|
|
|
|
procedure TJvCaptionButton.ResetButton;
|
|
begin
|
|
UnHook;
|
|
Hook;
|
|
Redraw(rkTotalCaptionBar);
|
|
end;
|
|
|
|
procedure TJvCaptionButton.SetAction(const Value: TBasicAction);
|
|
begin
|
|
if (FActionLink <> nil) and (FActionLink.Action <> nil) then
|
|
FActionLink.Action.RemoveFreeNotification(Self);
|
|
if Value = nil then
|
|
begin
|
|
FActionLink.Free;
|
|
FActionLink := nil;
|
|
end
|
|
else
|
|
begin
|
|
if FActionLink = nil then
|
|
FActionLink := GetActionLinkClass.Create(Self);
|
|
FActionLink.Action := Value;
|
|
FActionLink.OnChange := DoActionChange;
|
|
ActionChange(Value, csLoading in Value.ComponentState);
|
|
Value.FreeNotification(Self);
|
|
end;
|
|
end;
|
|
|
|
procedure TJvCaptionButton.SetAlignment(Value: TAlignment);
|
|
begin
|
|
if FAlignment <> Value then
|
|
begin
|
|
FAlignment := Value;
|
|
if Standard = tsbNone then
|
|
Redraw(rkIndirect);
|
|
end;
|
|
end;
|
|
|
|
procedure TJvCaptionButton.SetCaption(Value: string);
|
|
begin
|
|
if FCaption <> Value then
|
|
begin
|
|
FCaption := Value;
|
|
if Standard = tsbNone then
|
|
Redraw(rkIndirect);
|
|
end;
|
|
end;
|
|
|
|
procedure TJvCaptionButton.SetDown(const Value: Boolean);
|
|
begin
|
|
if (FDown <> Value) and Toggle then
|
|
begin
|
|
FDown := Value;
|
|
Redraw(rkIndirect);
|
|
end;
|
|
end;
|
|
|
|
procedure TJvCaptionButton.SetEnabled(const Value: Boolean);
|
|
begin
|
|
if FEnabled <> Value then
|
|
begin
|
|
FEnabled := Value;
|
|
Redraw(rkIndirect);
|
|
end;
|
|
end;
|
|
|
|
procedure TJvCaptionButton.SetFont(Value: TFont);
|
|
begin
|
|
if FFont <> Value then
|
|
begin
|
|
FFont.Assign(Value);
|
|
if Standard = tsbNone then
|
|
Redraw(rkIndirect);
|
|
end;
|
|
end;
|
|
|
|
{$IFDEF JVCLThemesEnabled}
|
|
procedure TJvCaptionButton.SetForceDrawSimple(const Value: Boolean);
|
|
begin
|
|
if FForceDrawSimple <> Value then
|
|
begin
|
|
FForceDrawSimple := Value;
|
|
if IsThemed then
|
|
Redraw(rkDirect);
|
|
end;
|
|
end;
|
|
{$ENDIF JVCLThemesEnabled}
|
|
|
|
procedure TJvCaptionButton.SetHeight(Value: Integer);
|
|
begin
|
|
if (FHeight <> Value) and (Value >= 0) then
|
|
begin
|
|
FHeight := Value;
|
|
Redraw(rkTotalCaptionBar);
|
|
end;
|
|
end;
|
|
|
|
procedure TJvCaptionButton.SetImageIndex(const Value: TImageIndex);
|
|
begin
|
|
if Value <> FImageIndex then
|
|
begin
|
|
FImageIndex := Value;
|
|
if Standard = tsbNone then
|
|
Redraw(rkIndirect);
|
|
end;
|
|
end;
|
|
|
|
procedure TJvCaptionButton.SetImages(const Value: TCustomImageList);
|
|
begin
|
|
ReplaceImageListReference(Self, Value, FImages, FImageChangeLink);
|
|
if Standard = tsbNone then
|
|
Redraw(rkIndirect);
|
|
end;
|
|
|
|
procedure TJvCaptionButton.SetLayout(const Value: TJvCaptionButtonLayout);
|
|
begin
|
|
if FLayout <> Value then
|
|
begin
|
|
FLayout := Value;
|
|
if (csDesigning in ComponentState) and (FAlignment <> taCenter) then
|
|
case FLayout of
|
|
cbImageLeft: FAlignment := taLeftJustify;
|
|
cbImageRight: FAlignment := taRightJustify;
|
|
end;
|
|
if (Standard = tsbNone) and IsImageVisible then
|
|
Redraw(rkIndirect);
|
|
end;
|
|
end;
|
|
|
|
procedure TJvCaptionButton.SetLeft(Value: Integer);
|
|
begin
|
|
if FLeft <> Value then
|
|
begin
|
|
FLeft := Value;
|
|
Redraw(rkTotalCaptionBar);
|
|
end;
|
|
end;
|
|
|
|
procedure TJvCaptionButton.SetMargin(const Value: Integer);
|
|
begin
|
|
if (FMargin <> Value) and (Value >= -1) then
|
|
begin
|
|
FMargin := Value;
|
|
if Standard = tsbNone then
|
|
Redraw(rkIndirect);
|
|
end;
|
|
end;
|
|
|
|
procedure TJvCaptionButton.SetMouseInControl(const Value: Boolean);
|
|
begin
|
|
if FMouseInControl <> Value then
|
|
begin
|
|
if not Value then
|
|
HideToolTip;
|
|
|
|
FMouseInControl := Value;
|
|
end;
|
|
end;
|
|
|
|
procedure TJvCaptionButton.SetParentShowHint(const Value: Boolean);
|
|
begin
|
|
if FParentShowHint <> Value then
|
|
begin
|
|
FParentShowHint := Value;
|
|
if FParentShowHint then
|
|
FShowHint := ParentForm.ShowHint;
|
|
end;
|
|
end;
|
|
|
|
procedure TJvCaptionButton.SetPosition(const Value: Integer);
|
|
begin
|
|
if FPosition <> Value then
|
|
begin
|
|
FPosition := Value;
|
|
Redraw(rkTotalCaptionBar);
|
|
end;
|
|
end;
|
|
|
|
procedure TJvCaptionButton.SetShowHint(const Value: Boolean);
|
|
begin
|
|
if FShowHint <> Value then
|
|
begin
|
|
FShowHint := Value;
|
|
FParentShowHint := False;
|
|
end;
|
|
end;
|
|
|
|
procedure TJvCaptionButton.SetSpacing(const Value: Integer);
|
|
begin
|
|
if FSpacing <> Value then
|
|
begin
|
|
FSpacing := Value;
|
|
if Standard = tsbNone then
|
|
Redraw(rkIndirect);
|
|
end;
|
|
end;
|
|
|
|
procedure TJvCaptionButton.SetStandard(Value: TJvStandardButton);
|
|
{$IFDEF JVCLThemesEnabled}
|
|
var
|
|
ButtonSizeChanged: Boolean;
|
|
{$ENDIF JVCLThemesEnabled}
|
|
begin
|
|
if FStandard <> Value then
|
|
begin
|
|
{$IFDEF JVCLThemesEnabled}
|
|
ButtonSizeChanged := IsThemed and
|
|
((Value = tsbMinimizeToTray) or (FStandard = tsbMinimizeToTray));
|
|
{$ENDIF JVCLThemesEnabled}
|
|
FStandard := Value;
|
|
|
|
{$IFDEF JVCLThemesEnabled}
|
|
if ButtonSizeChanged then
|
|
UpdateButtonRect(ParentFormHandle);
|
|
|
|
if ButtonSizeChanged and (FStandard = tsbMinimizeToTray) then
|
|
Redraw(rkTotalCaptionBar)
|
|
else
|
|
{$ENDIF JVCLThemesEnabled}
|
|
Redraw(rkIndirect);
|
|
end;
|
|
end;
|
|
|
|
procedure TJvCaptionButton.SetToggle(const Value: Boolean);
|
|
begin
|
|
if FToggle <> Value then
|
|
begin
|
|
FToggle := Value;
|
|
if FDown and not FToggle and not (FMouseInControl and FMouseButtonDown) then
|
|
begin
|
|
FDown := False;
|
|
Redraw(rkIndirect);
|
|
end;
|
|
end;
|
|
end;
|
|
|
|
procedure TJvCaptionButton.SetTop(Value: Integer);
|
|
begin
|
|
if FTop <> Value then
|
|
begin
|
|
FTop := Value;
|
|
Redraw(rkTotalCaptionBar);
|
|
end;
|
|
end;
|
|
|
|
procedure TJvCaptionButton.SetVisible(const Value: Boolean);
|
|
begin
|
|
if Value <> FVisible then
|
|
begin
|
|
FVisible := Value;
|
|
{ Caption, RedrawButton doesn't draw the caption itself }
|
|
Redraw(rkTotalCaptionBar);
|
|
end;
|
|
end;
|
|
|
|
procedure TJvCaptionButton.SetWidth(Value: Integer);
|
|
begin
|
|
if (FWidth <> Value) and (Value >= 0) then
|
|
begin
|
|
FWidth := Value;
|
|
Redraw(rkTotalCaptionBar);
|
|
end;
|
|
end;
|
|
|
|
procedure TJvCaptionButton.UnHook;
|
|
var
|
|
P: TCustomForm;
|
|
begin
|
|
P := ParentForm;
|
|
if Assigned(P) then
|
|
begin
|
|
DestroyToolTip;
|
|
UnRegisterWndProcHook(P, WndProcAfter, hoAfterMsg);
|
|
UnRegisterWndProcHook(P, WndProcBefore, hoBeforeMsg);
|
|
end;
|
|
end;
|
|
|
|
procedure TJvCaptionButton.UpdateButtonRect(Wnd: THandle);
|
|
var
|
|
WindowRect: TRect;
|
|
LButtonWidth: Integer;
|
|
LButtonHeight: Integer;
|
|
begin
|
|
if Wnd = 0 then
|
|
Exit;
|
|
|
|
if FNeedRecalculate then
|
|
CalcDefaultButtonRect(Wnd);
|
|
|
|
GetWindowRect(Wnd, WindowRect);
|
|
|
|
if ButtonWidth <> 0 then
|
|
LButtonWidth := ButtonWidth
|
|
else
|
|
LButtonWidth := FDefaultButtonWidth;
|
|
|
|
if ButtonHeight <> 0 then
|
|
LButtonHeight := ButtonHeight
|
|
else
|
|
LButtonHeight := FDefaultButtonHeight;
|
|
|
|
FButtonRect := Bounds(
|
|
WindowRect.Right - WindowRect.Left - FDefaultButtonLeft + ButtonLeft
|
|
+ FDefaultButtonWidth - LButtonWidth,
|
|
FDefaultButtonTop + ButtonTop, LButtonWidth, LButtonHeight);
|
|
|
|
OffsetRect(FButtonRect, -FPosition * (FDefaultButtonWidth + 2), 0);
|
|
|
|
{ Special }
|
|
{$IFDEF JVCLThemesEnabled}
|
|
if (FStandard = tsbMinimizeToTray) and IsThemed then
|
|
Inc(FButtonRect.Top, 2);
|
|
{$ENDIF JVCLThemesEnabled}
|
|
|
|
{ Click rect is a bit bigger }
|
|
with FButtonRect do
|
|
FClickRect := Rect(Left - 2, Top - 2, Right + 1, Bottom + 2);
|
|
end;
|
|
|
|
function TJvCaptionButton.WndProcAfter(var Msg: TMessage): Boolean;
|
|
begin
|
|
{ let others listen in too }
|
|
Result := False;
|
|
|
|
case Msg.Msg of
|
|
{$IFDEF JVCLThemesEnabled}
|
|
WM_THEMECHANGED,
|
|
{$ENDIF JVCLThemesEnabled}
|
|
CM_SYSCOLORCHANGE:
|
|
begin
|
|
FNeedRecalculate := True;
|
|
{$IFDEF JVCLThemesEnabled}
|
|
{ force theme data refresh, needed when
|
|
|
|
* Switching from 'windows classic' style to 'windows XP' style
|
|
( delphi 7 bug) }
|
|
ThemeServices.UpdateThemes;
|
|
GlobalXPData.Update;
|
|
{$ENDIF JVCLThemesEnabled}
|
|
end;
|
|
CM_SYSFONTCHANGED:
|
|
begin
|
|
FNeedRecalculate := True;
|
|
{$IFDEF JVCLThemesEnabled}
|
|
{ force theme data refresh, needed when
|
|
|
|
* Non-themed application and switching system font size }
|
|
if not ThemeServices.ThemesEnabled then
|
|
ThemeServices.UpdateThemes;
|
|
{$ENDIF JVCLThemesEnabled}
|
|
end;
|
|
WM_SETTEXT:
|
|
{ Caption text may overwrite the button, so redraw }
|
|
Redraw(rkIndirect);
|
|
WM_NCPAINT:
|
|
HandleNCPaintAfter(ParentFormHandle, TWMNCPaint(Msg));
|
|
WM_NCACTIVATE:
|
|
HandleNCActivate(TWMNCActivate(Msg));
|
|
CM_RECREATEWND:
|
|
begin
|
|
CreateToolTip(ParentFormHandle);
|
|
FNeedRecalculate := True;
|
|
//CalcDefaultButtonRect(ParentFormHandle);
|
|
Redraw(rkTotalCaptionBar);
|
|
end;
|
|
WM_LBUTTONDOWN, WM_NCLBUTTONUP, WM_LBUTTONUP, WM_NCMOUSEMOVE, WM_NCRBUTTONUP,
|
|
WM_RBUTTONUP, WM_NCRBUTTONDOWN, WM_RBUTTONDOWN, WM_NCLBUTTONDOWN:
|
|
ForwardToToolTip(Msg);
|
|
end;
|
|
end;
|
|
|
|
function TJvCaptionButton.WndProcBefore(var Msg: TMessage): Boolean;
|
|
begin
|
|
case Msg.Msg of
|
|
CM_SHOWHINTCHANGED:
|
|
begin
|
|
if ParentShowHint then
|
|
FShowHint := ParentForm.ShowHint;
|
|
Result := False;
|
|
end;
|
|
CM_MOUSELEAVE, CM_MOUSEENTER:
|
|
begin
|
|
if FMouseInControl then
|
|
begin
|
|
SetMouseInControl(False);
|
|
Redraw(rkIndirect);
|
|
end;
|
|
Result := False;
|
|
end;
|
|
WM_NCLBUTTONDOWN: //, WM_LBUTTONDOWN:
|
|
begin
|
|
Result := HandleButtonDown(TWMNCHitMessage(Msg));
|
|
if Result then
|
|
ForwardToToolTip(Msg);
|
|
end;
|
|
WM_NCLBUTTONUP, WM_LBUTTONUP:
|
|
begin
|
|
Result := HandleButtonUp(TWMNCHitMessage(Msg));
|
|
if Result then
|
|
ForwardToToolTip(Msg);
|
|
end;
|
|
WM_MOUSEMOVE:
|
|
begin
|
|
Result := HandleMouseMove(TWMNCHitMessage(Msg));
|
|
if Result then
|
|
ForwardToToolTip(Msg);
|
|
end;
|
|
WM_NCMOUSEMOVE:
|
|
begin
|
|
Result := False;
|
|
HandleNCMouseMove(TWMNCHitMessage(Msg));
|
|
end;
|
|
WM_NCHITTEST:
|
|
Result := HandleHitTest(TWMNCHitTest(Msg));
|
|
WM_NCPAINT:
|
|
begin
|
|
Result := False;
|
|
HandleNCPaintBefore(ParentFormHandle, TWMNCPaint(Msg));
|
|
end;
|
|
WM_DESTROY:
|
|
begin
|
|
Result := False;
|
|
{DestroyToolTip;}
|
|
// FToolTipHandle is automatically destroyed when ParentForm handle is destroyed.
|
|
FToolTipHandle := 0;
|
|
end;
|
|
WM_NOTIFY:
|
|
Result := HandleNotify(TWMNotify(Msg));
|
|
WM_SIZE:
|
|
begin
|
|
if FCurrentWindowState <> ParentForm.WindowState then
|
|
begin
|
|
FNeedRecalculate := True;
|
|
FCurrentWindowState := ParentForm.WindowState;
|
|
RedrawWindow(ParentFormHandle, nil, 0, RDW_FRAME or RDW_INVALIDATE);
|
|
end;
|
|
Result := False;
|
|
end;
|
|
else
|
|
Result := False;
|
|
end;
|
|
end;
|
|
|
|
//=== { TJvCaptionButtonActionLink } =========================================
|
|
|
|
procedure TJvCaptionButtonActionLink.AssignClient(AClient: TObject);
|
|
begin
|
|
FClient := AClient as TJvCaptionButton;
|
|
end;
|
|
|
|
function TJvCaptionButtonActionLink.IsCaptionLinked: Boolean;
|
|
begin
|
|
Result := inherited IsCaptionLinked and
|
|
AnsiSameText(FClient.Caption, (Action as TCustomAction).Caption);
|
|
end;
|
|
|
|
function TJvCaptionButtonActionLink.IsEnabledLinked: Boolean;
|
|
begin
|
|
Result := inherited IsEnabledLinked and
|
|
(FClient.Enabled = (Action as TCustomAction).Enabled);
|
|
end;
|
|
|
|
function TJvCaptionButtonActionLink.IsHintLinked: Boolean;
|
|
begin
|
|
Result := inherited IsHintLinked and
|
|
(FClient.Hint = (Action as TCustomAction).Hint);
|
|
end;
|
|
|
|
function TJvCaptionButtonActionLink.IsImageIndexLinked: Boolean;
|
|
begin
|
|
Result := inherited IsImageIndexLinked and
|
|
(FClient.ImageIndex = (Action as TCustomAction).ImageIndex);
|
|
end;
|
|
|
|
function TJvCaptionButtonActionLink.IsOnExecuteLinked: Boolean;
|
|
begin
|
|
Result := inherited IsOnExecuteLinked and
|
|
(@FClient.OnClick = @Action.OnExecute);
|
|
end;
|
|
|
|
function TJvCaptionButtonActionLink.IsVisibleLinked: Boolean;
|
|
begin
|
|
Result := inherited IsVisibleLinked and
|
|
(FClient.Visible = (Action as TCustomAction).Visible);
|
|
end;
|
|
|
|
procedure TJvCaptionButtonActionLink.SetCaption(const Value: string);
|
|
begin
|
|
if IsCaptionLinked then
|
|
FClient.Caption := Value;
|
|
end;
|
|
|
|
procedure TJvCaptionButtonActionLink.SetEnabled(Value: Boolean);
|
|
begin
|
|
if IsEnabledLinked then
|
|
FClient.Enabled := Value;
|
|
end;
|
|
|
|
procedure TJvCaptionButtonActionLink.SetHint(const Value: string);
|
|
begin
|
|
if IsHintLinked then
|
|
FClient.Hint := Value;
|
|
end;
|
|
|
|
procedure TJvCaptionButtonActionLink.SetImageIndex(Value: Integer);
|
|
begin
|
|
if IsImageIndexLinked then
|
|
FClient.ImageIndex := Value;
|
|
end;
|
|
|
|
procedure TJvCaptionButtonActionLink.SetOnExecute(Value: TNotifyEvent);
|
|
begin
|
|
if IsOnExecuteLinked then
|
|
FClient.OnClick := Value;
|
|
end;
|
|
|
|
procedure TJvCaptionButtonActionLink.SetVisible(Value: Boolean);
|
|
begin
|
|
if IsVisibleLinked then
|
|
FClient.Visible := Value;
|
|
end;
|
|
|
|
initialization
|
|
{$IFDEF UNITVERSIONING}
|
|
RegisterUnitVersion(HInstance, UnitVersioning);
|
|
{$ENDIF UNITVERSIONING}
|
|
|
|
finalization
|
|
UnloadMsimg32Dll;
|
|
{$IFDEF UNITVERSIONING}
|
|
UnregisterUnitVersion(HInstance);
|
|
{$ENDIF UNITVERSIONING}
|
|
|
|
end.
|