home
***
CD-ROM
|
disk
|
FTP
|
other
***
search
/
Chip 2001 September
/
Chip_2001-09_cd1.bin
/
zkuste
/
delphi
/
kompon
/
d23456
/
TB97.ZIP
/
Source
/
TB97Ctls.pas
< prev
next >
Wrap
Pascal/Delphi Source File
|
2001-06-18
|
95KB
|
2,943 lines
unit TB97Ctls;
{
Toolbar97
Copyright (C) 1998-2001 by Jordan Russell
For conditions of distribution and use, see LICENSE.TXT.
TToolbarButton97 & TEdit97
$Id: TB97Ctls.pas,v 1.8 2001/05/01 17:00:49 jr Exp $
}
interface
{$I TB97Ver.inc}
uses
Windows, Messages, Classes, Controls, Forms, Menus, Graphics, Buttons,
{$IFDEF TB97D4} ImgList, ActnList, {$ENDIF} StdCtrls, ExtCtrls,
TB97Vers;
const
DefaultDropdownArrowWidth = 9;
type
{ TToolbarButton97 }
TButtonDisplayMode = (dmBoth, dmGlyphOnly, dmTextOnly);
TButtonState97 = (bsUp, bsDisabled, bsDown, bsExclusive, bsMouseIn);
TNumGlyphs97 = 1..5;
TButtonDropdownEvent = procedure(Sender: TObject;
var ShowMenu, RemoveClicks: Boolean) of object;
TToolbarButton97 = class(TGraphicControl)
private
FAllowAllUp: Boolean;
FAlignment: TAlignment;
FCancel: Boolean;
FDefault: Boolean;
FDisplayMode: TButtonDisplayMode;
FDown: Boolean;
FDropdownAlways: Boolean;
FDropdownArrow: Boolean;
FDropdownArrowWidth: Integer;
FDropdownCombo: Boolean;
FDropdownMenu: TPopupMenu;
FFlat: Boolean;
FGlyph: Pointer;
FGroupIndex: Integer;
FHelpContext: THelpContext;
FHighlightWhenDown: Boolean;
FLayout: TButtonLayout;
FMargin: Integer;
FModalResult: TModalResult;
FNoBorder: Boolean;
FOldDisabledStyle: Boolean;
FOpaque: Boolean;
FRepeating: Boolean;
FRepeatDelay, FRepeatInterval: Integer;
FShowBorderWhenInactive: Boolean;
FSpacing: Integer;
FWordWrap: Boolean;
FOnDropdown: TButtonDropdownEvent;
FOnMouseEnter, FOnMouseExit: TNotifyEvent;
{ Internal }
FInClick: Boolean;
FMouseInControl: Boolean;
FMouseIsDown: Boolean;
FMenuIsDown: Boolean;
FUsesDropdown: Boolean;
FRepeatTimer: TTimer;
procedure GlyphChanged(Sender: TObject);
procedure UpdateExclusive;
procedure SetAlignment (Value: TAlignment);
procedure SetAllowAllUp (Value: Boolean);
function GetCallDormant: Boolean;
procedure SetCallDormant (Value: Boolean);
procedure SetDown (Value: Boolean);
procedure SetDisplayMode (Value: TButtonDisplayMode);
procedure SetDropdownAlways (Value: Boolean);
procedure SetDropdownArrow (Value: Boolean);
procedure SetDropdownArrowWidth (Value: Integer);
procedure SetDropdownCombo (Value: Boolean);
procedure SetDropdownMenu (Value: TPopupMenu);
procedure SetFlat (Value: Boolean);
function GetGlyph: TBitmap;
procedure SetGlyph (Value: TBitmap);
function GetGlyphMask: TBitmap;
procedure SetGlyphMask (Value: TBitmap);
procedure SetGroupIndex (Value: Integer);
procedure SetHighlightWhenDown (Value: Boolean);
function GetImageIndex: Integer;
procedure SetImageIndex (Value: Integer);
function GetImages: TCustomImageList;
procedure SetImages (Value: TCustomImageList);
procedure SetLayout (Value: TButtonLayout);
procedure SetMargin (Value: Integer);
procedure SetNoBorder (Value: Boolean);
function GetNumGlyphs: TNumGlyphs97;
procedure SetNumGlyphs (Value: TNumGlyphs97);
procedure SetOldDisabledStyle (Value: Boolean);
procedure SetOpaque (Value: Boolean);
procedure SetSpacing (Value: Integer);
function GetVersion: TToolbar97Version;
procedure SetVersion (const Value: TToolbar97Version);
procedure SetWordWrap (Value: Boolean);
procedure RemoveButtonMouseTimer;
procedure Redraw (const Erase: Boolean);
function PointInButton (X, Y: Integer): Boolean;
procedure ButtonMouseTimerHandler (Sender: TObject);
procedure RepeatTimerHandler (Sender: TObject);
{$IFDEF TB97D4}
function IsCheckedStored: Boolean;
function IsHelpContextStored: Boolean;
function IsImageIndexStored: Boolean;
{$ENDIF}
procedure WMLButtonDblClk (var Message: TWMLButtonDblClk); message WM_LBUTTONDBLCLK;
procedure CMEnabledChanged (var Message: TMessage); message CM_ENABLEDCHANGED;
procedure CMDialogChar (var Message: TCMDialogChar); message CM_DIALOGCHAR;
procedure CMDialogKey (var Message: TCMDialogKey); message CM_DIALOGKEY;
procedure CMFontChanged (var Message: TMessage); message CM_FONTCHANGED;
procedure CMTextChanged (var Message: TMessage); message CM_TEXTCHANGED;
procedure CMSysColorChange (var Message: TMessage); message CM_SYSCOLORCHANGE;
procedure WMCancelMode (var Message: TWMCancelMode); message WM_CANCELMODE;
protected
FState: TButtonState97;
function GetPalette: HPALETTE; override;
procedure Loaded; override;
procedure Notification (AComponent: TComponent; Operation: TOperation); override;
procedure MouseDown (Button: TMouseButton; Shift: TShiftState;
X, Y: Integer); override;
procedure MouseMove (Shift: TShiftState; X, Y: Integer); override;
procedure MouseUp (Button: TMouseButton; Shift: TShiftState;
X, Y: Integer); override;
procedure Paint; override;
{$IFDEF TB97D4}
procedure ActionChange (Sender: TObject; CheckDefaults: Boolean); override;
function GetActionLinkClass: TControlActionLinkClass; override;
procedure AssignTo (Dest: TPersistent); override;
{$ENDIF}
public
property Canvas;
property CallDormant: Boolean read GetCallDormant write SetCallDormant;
constructor Create(AOwner: TComponent); override;
destructor Destroy; override;
procedure Click; override;
procedure MouseEntered;
procedure MouseLeft;
published
{$IFDEF TB97D4}
property Action;
{$ENDIF}
property Alignment: TAlignment read FAlignment write SetAlignment default taCenter;
property AllowAllUp: Boolean read FAllowAllUp write SetAllowAllUp default False;
{$IFDEF TB97D4}
property Anchors;
{$ENDIF}
property Cancel: Boolean read FCancel write FCancel default False;
property Color default clBtnFace;
{$IFDEF TB97D4}
property Constraints;
{$ENDIF}
property GroupIndex: Integer read FGroupIndex write SetGroupIndex default 0;
property Default: Boolean read FDefault write FDefault default False;
property DisplayMode: TButtonDisplayMode read FDisplayMode write SetDisplayMode default dmBoth;
property Down: Boolean read FDown write SetDown {$IFDEF TB97D4} stored IsCheckedStored {$ENDIF} default False;
property DragCursor;
property DragMode;
property DropdownAlways: Boolean read FDropdownAlways write SetDropdownAlways default False;
property DropdownArrow: Boolean read FDropdownArrow write SetDropdownArrow default True;
property DropdownArrowWidth: Integer read FDropdownArrowWidth write SetDropdownArrowWidth default DefaultDropdownArrowWidth;
property DropdownCombo: Boolean read FDropdownCombo write SetDropdownCombo default False;
property DropdownMenu: TPopupMenu read FDropdownMenu write SetDropdownMenu;
property Caption;
property Enabled;
property Flat: Boolean read FFlat write SetFlat default True;
property Font;
property Glyph: TBitmap read GetGlyph write SetGlyph;
property GlyphMask: TBitmap read GetGlyphMask write SetGlyphMask;
property HelpContext: THelpContext read FHelpContext write FHelpContext {$IFDEF TB97D4} stored IsHelpContextStored {$ENDIF} default 0;
property HighlightWhenDown: Boolean read FHighlightWhenDown write SetHighlightWhenDown default True;
property ImageIndex: Integer read GetImageIndex write SetImageIndex {$IFDEF TB97D4} stored IsImageIndexStored {$ENDIF} default -1;
property Images: TCustomImageList read GetImages write SetImages;
property Layout: TButtonLayout read FLayout write SetLayout default blGlyphLeft;
property Margin: Integer read FMargin write SetMargin default -1;
property ModalResult: TModalResult read FModalResult write FModalResult default 0;
property NoBorder: Boolean read FNoBorder write SetNoBorder default False;
property NumGlyphs: TNumGlyphs97 read GetNumGlyphs write SetNumGlyphs default 1;
property OldDisabledStyle: Boolean read FOldDisabledStyle write SetOldDisabledStyle default False;
property Opaque: Boolean read FOpaque write SetOpaque default True;
property ParentFont;
property ParentColor default False;
property ParentShowHint;
property Repeating: Boolean read FRepeating write FRepeating default False;
property RepeatDelay: Integer read FRepeatDelay write FRepeatDelay default 400;
property RepeatInterval: Integer read FRepeatInterval write FRepeatInterval default 100;
property ShowBorderWhenInactive: Boolean read FShowBorderWhenInactive write FShowBorderWhenInactive default False;
property ShowHint;
property Spacing: Integer read FSpacing write SetSpacing default 4;
property Version: TToolbar97Version read GetVersion write SetVersion stored False;
property Visible;
property WordWrap: Boolean read FWordWrap write SetWordWrap default False;
property OnClick;
property OnDblClick;
property OnDragDrop;
property OnDragOver;
property OnDropdown: TButtonDropdownEvent read FOnDropdown write FOnDropdown;
property OnEndDrag;
property OnMouseDown;
property OnMouseEnter: TNotifyEvent read FOnMouseEnter write FOnMouseEnter;
property OnMouseExit: TNotifyEvent read FOnMouseExit write FOnMouseExit;
property OnMouseMove;
property OnMouseUp;
property OnStartDrag;
end;
{ TToolButtonActionLink }
{$IFDEF TB97D4}
TToolbarButton97ActionLink = class(TControlActionLink)
protected
FClient: TToolbarButton97;
procedure AssignClient (AClient: TObject); override;
function IsCheckedLinked: Boolean; override;
function IsHelpContextLinked: Boolean; override;
function IsImageIndexLinked: Boolean; override;
procedure SetChecked (Value: Boolean); override;
procedure SetHelpContext (Value: THelpContext); override;
procedure SetImageIndex (Value: Integer); override;
end;
TToolbarButton97ActionLinkClass = class of TToolbarButton97ActionLink;
{$ENDIF}
{ TEdit97 }
TEdit97 = class(TCustomEdit)
private
MouseInControl: Boolean;
function GetVersion: TToolbar97Version;
procedure SetVersion (const Value: TToolbar97Version);
procedure DrawNCArea (const DrawToDC: Boolean; const ADC: HDC;
const Clip: HRGN);
procedure NewAdjustHeight;
procedure CMEnabledChanged (var Message: TMessage); message CM_ENABLEDCHANGED;
procedure CMFontChanged (var Message: TMessage); message CM_FONTCHANGED;
procedure CMMouseEnter (var Message: TMessage); message CM_MOUSEENTER;
procedure CMMouseLeave (var Message: TMessage); message CM_MOUSELEAVE;
procedure WMKillFocus (var Message: TWMKillFocus); message WM_KILLFOCUS;
procedure WMNCCalcSize (var Message: TWMNCCalcSize); message WM_NCCALCSIZE;
procedure WMNCPaint (var Message: TMessage); message WM_NCPAINT;
procedure WMPrint (var Message: TMessage); message WM_PRINT;
procedure WMPrintClient (var Message: TMessage); message WM_PRINTCLIENT;
procedure WMSetFocus (var Message: TWMSetFocus); message WM_SETFOCUS;
protected
procedure Loaded; override;
public
constructor Create (AOwner: TComponent); override;
destructor Destroy; override;
published
property AutoSelect;
{$IFDEF TB97D4}
property Anchors;
{$ENDIF}
property Align;
{$IFDEF TB97D4}
property BiDiMode;
{$ENDIF}
property CharCase;
{$IFDEF TB97D4}
property Constraints;
{$ENDIF}
property DragCursor;
{$IFDEF TB97D4}
property DragKind;
{$ENDIF}
property DragMode;
property Enabled;
property Font;
property HideSelection;
{$IFDEF TB97D3}
property ImeMode;
property ImeName;
{$ENDIF}
property MaxLength;
property OEMConvert;
{$IFDEF TB97D4}
property ParentBiDiMode;
{$ENDIF}
property ParentColor;
property ParentCtl3D;
property ParentFont;
property ParentShowHint;
property PasswordChar;
property PopupMenu;
property ReadOnly;
property ShowHint;
property TabOrder;
property TabStop;
property Text;
property Version: TToolbar97Version read GetVersion write SetVersion stored False;
property Visible;
property OnChange;
property OnClick;
property OnDblClick;
property OnDragDrop;
property OnDragOver;
{$IFDEF TB97D4}
property OnEndDock;
{$ENDIF}
property OnEndDrag;
property OnEnter;
property OnExit;
property OnKeyDown;
property OnKeyPress;
property OnKeyUp;
property OnMouseDown;
property OnMouseMove;
property OnMouseUp;
{$IFDEF TB97D4}
property OnStartDock;
{$ENDIF}
property OnStartDrag;
end;
var
ButtonsStayDown: Boolean = True;
ButtonMouseInControl: TToolbarButton97 = nil;
function ControlIs97Control (AControl: TControl): Boolean;
procedure Register97ControlClass (AClass: TClass);
procedure Unregister97ControlClass (AClass: TClass);
implementation
uses
SysUtils, Consts, CommCtrl, TB97Cmn;
var
{ See TToolbarButton97.ButtonMouseTimerHandler for info on this }
ButtonMouseTimer: TTimer = nil;
Control97List: TList = nil;
Edit97Count: Integer = 0;
const
DropdownComboSpace = 2;
function ControlIs97Control (AControl: TControl): Boolean;
var
I: Integer;
begin
Result := False;
if Assigned(AControl) and Assigned(Control97List) then
for I := 0 to Control97List.Count-1 do
if AControl is TClass(Control97List[I]) then begin
Result := True;
Break;
end;
end;
procedure Register97ControlClass (AClass: TClass);
begin
if Control97List = nil then Control97List := TList.Create;
Control97List.Add (AClass);
end;
procedure Unregister97ControlClass (AClass: TClass);
begin
if Assigned(Control97List) then begin
Control97List.Remove (AClass);
if Control97List.Count = 0 then begin
Control97List.Free;
Control97List := nil;
end;
end;
end;
{ TToolbarButton97ActionLink - internal }
{$IFDEF TB97D4}
procedure TToolbarButton97ActionLink.AssignClient (AClient: TObject);
begin
inherited AssignClient(AClient);
FClient := AClient as TToolbarButton97;
end;
function TToolbarButton97ActionLink.IsCheckedLinked: Boolean;
begin
Result := inherited IsCheckedLinked and
(FClient.Down = (Action as TCustomAction).Checked);
end;
function TToolbarButton97ActionLink.IsHelpContextLinked: Boolean;
begin
Result := inherited IsHelpContextLinked and
(FClient.HelpContext = (Action as TCustomAction).HelpContext);
end;
function TToolbarButton97ActionLink.IsImageIndexLinked: Boolean;
begin
Result := inherited IsImageIndexLinked and
(FClient.ImageIndex = (Action as TCustomAction).ImageIndex);
end;
procedure TToolbarButton97ActionLink.SetChecked (Value: Boolean);
begin
if IsCheckedLinked then FClient.Down := Value;
end;
procedure TToolbarButton97ActionLink.SetHelpContext (Value: THelpContext);
begin
if IsHelpContextLinked then FClient.HelpContext := Value;
end;
procedure TToolbarButton97ActionLink.SetImageIndex (Value: Integer);
begin
if IsImageIndexLinked then FClient.ImageIndex := Value;
end;
{$ENDIF}
{ TToolbarButton97 - internal }
type
TGlyphList = class(TImageList)
private
Used: TBits;
FCount: Integer;
function AllocateIndex: Integer;
public
constructor CreateSize (AWidth, AHeight: Integer);
destructor Destroy; override;
function Add (Image, Mask: TBitmap): Integer;
function AddMasked (Image: TBitmap; MaskColor: TColor): Integer;
procedure Delete (Index: Integer);
property Count: Integer read FCount;
end;
TGlyphCache = class
private
GlyphLists: TList;
public
constructor Create;
destructor Destroy; override;
function GetList(AWidth, AHeight: Integer): TGlyphList;
procedure ReturnList(List: TGlyphList);
function Empty: Boolean;
end;
TBoolInt = record
B: Boolean;
I: Integer;
end;
TCustomImageListAccess = class(TCustomImageList);
TButtonGlyph = class
private
FOriginal, FOriginalMask: TBitmap;
FCallDormant: Boolean;
FGlyphList: array[Boolean] of TGlyphList;
FImageIndex: Integer;
FImageList: TCustomImageList;
FImageChangeLink: TChangeLink;
FIndexs: array[Boolean, TButtonState97] of Integer;
FTransparentColor: TColor;
FNumGlyphs: TNumGlyphs97;
FOnChange: TNotifyEvent;
FOldDisabledStyle: Boolean;
procedure GlyphChanged (Sender: TObject);
procedure SetGlyph (Value: TBitmap);
procedure SetGlyphMask (Value: TBitmap);
procedure SetNumGlyphs (Value: TNumGlyphs97);
procedure UpdateNumGlyphs;
procedure Invalidate;
function CreateButtonGlyph (State: TButtonState97): TBoolInt;
procedure DrawButtonGlyph (Canvas: TCanvas; const GlyphPos: TPoint;
State: TButtonState97);
procedure DrawButtonText (Canvas: TCanvas;
const Caption: string; TextBounds: TRect;
WordWrap: Boolean; Alignment: TAlignment; State: TButtonState97);
procedure DrawButtonDropArrow (Canvas: TCanvas; const X, Y, AWidth: Integer;
State: TButtonState97);
procedure CalcButtonLayout (Canvas: TCanvas; const Client: TRect;
const Offset: TPoint; DrawGlyph, DrawCaption: Boolean;
const Caption: string; WordWrap: Boolean;
Layout: TButtonLayout; Margin, Spacing: Integer; DropArrow: Boolean;
DropArrowWidth: Integer; var GlyphPos, ArrowPos: TPoint;
var TextBounds: TRect);
public
constructor Create;
destructor Destroy; override;
{ returns the text rectangle }
function Draw (Canvas: TCanvas; const Client: TRect; const Offset: TPoint;
DrawGlyph, DrawCaption: Boolean; const Caption: string; WordWrap: Boolean;
Alignment: TAlignment; Layout: TButtonLayout; Margin, Spacing: Integer;
DropArrow: Boolean; DropArrowWidth: Integer; State: TButtonState97): TRect;
property Glyph: TBitmap read FOriginal write SetGlyph;
property GlyphMask: TBitmap read FOriginalMask write SetGlyphMask;
property NumGlyphs: TNumGlyphs97 read FNumGlyphs write SetNumGlyphs;
property OnChange: TNotifyEvent read FOnChange write FOnChange;
end;
{ TGlyphList }
constructor TGlyphList.CreateSize(AWidth, AHeight: Integer);
begin
inherited CreateSize (AWidth, AHeight);
Used := TBits.Create;
end;
destructor TGlyphList.Destroy;
begin
Used.Free;
inherited;
end;
function TGlyphList.AllocateIndex: Integer;
begin
Result := Used.OpenBit;
if Result >= Used.Size then
begin
Result := inherited Add(nil, nil);
Used.Size := Result + 1;
end;
Used[Result] := True;
end;
function TGlyphList.Add (Image, Mask: TBitmap): Integer;
begin
Result := AllocateIndex;
Replace (Result, Image, Mask);
Inc (FCount);
end;
function TGlyphList.AddMasked (Image: TBitmap; MaskColor: TColor): Integer;
procedure BugfreeReplaceMasked (Index: Integer; NewImage: TBitmap; MaskColor: TColor);
procedure CheckImage (Image: TGraphic);
begin
if Image = nil then Exit;
if (Image.Height < Height) or (Image.Width < Width) then
raise EInvalidOperation.Create({$IFNDEF TB97D3}LoadStr{$ENDIF}(SInvalidImageSize));
end;
var
TempIndex: Integer;
Image, Mask: TBitmap;
begin
if HandleAllocated then begin
CheckImage(NewImage);
TempIndex := inherited AddMasked(NewImage, MaskColor);
if TempIndex <> -1 then
try
Image := nil;
Mask := nil;
try
Image := TBitmap.Create;
Image.Height := Height;
Image.Width := Width;
Mask := TBitmap.Create;
Mask.Monochrome := True;
{ ^ Prevents the "invisible glyph" problem when used with certain
color schemes. (Fixed in Delphi 3.01) }
Mask.Height := Height;
Mask.Width := Width;
ImageList_Draw (Handle, TempIndex, Image.Canvas.Handle, 0, 0, ILD_NORMAL);
ImageList_Draw (Handle, TempIndex, Mask.Canvas.Handle, 0, 0, ILD_MASK);
if not ImageList_Replace(Handle, Index, Image.Handle, Mask.Handle) then
raise EInvalidOperation.Create({$IFNDEF TB97D3}LoadStr{$ENDIF}(SReplaceImage));
finally
Image.Free;
Mask.Free;
end;
finally
inherited Delete(TempIndex);
end
else
raise EInvalidOperation.Create({$IFNDEF TB97D3}LoadStr{$ENDIF}(SReplaceImage));
end;
Change;
end;
begin
Result := AllocateIndex;
{ This works two very serious bugs in the Delphi 2/BCB and Delphi 3
implementations of the ReplaceMasked method. In the Delphi 2 and BCB
versions of the ReplaceMasked method, it incorrectly uses ILD_NORMAL as
the last parameter for the second ImageList_Draw call, in effect causing
all white colors to be considered transparent also. And in the Delphi 2/3
and BCB versions it doesn't set Monochrome to True on the Mask bitmap,
causing the bitmaps to be invisible on certain color schemes. }
BugfreeReplaceMasked (Result, Image, MaskColor);
Inc (FCount);
end;
procedure TGlyphList.Delete (Index: Integer);
begin
if Used[Index] then begin
Dec(FCount);
Used[Index] := False;
end;
end;
{ TGlyphCache }
constructor TGlyphCache.Create;
begin
inherited;
GlyphLists := TList.Create;
end;
destructor TGlyphCache.Destroy;
begin
GlyphLists.Free;
inherited;
end;
function TGlyphCache.GetList(AWidth, AHeight: Integer): TGlyphList;
var
I: Integer;
begin
for I := GlyphLists.Count - 1 downto 0 do begin
Result := GlyphLists[I];
with Result do
if (AWidth = Width) and (AHeight = Height) then Exit;
end;
Result := TGlyphList.CreateSize(AWidth, AHeight);
GlyphLists.Add(Result);
end;
procedure TGlyphCache.ReturnList(List: TGlyphList);
begin
if List = nil then Exit;
if List.Count = 0 then begin
GlyphLists.Remove(List);
List.Free;
end;
end;
function TGlyphCache.Empty: Boolean;
begin
Result := GlyphLists.Count = 0;
end;
var
GlyphCache: TGlyphCache = nil;
Pattern: TBitmap = nil;
PatternBtnFace, PatternBtnHighlight: TColor;
ButtonCount: Integer = 0;
procedure CreateBrushPattern;
var
X, Y: Integer;
begin
PatternBtnFace := GetSysColor(COLOR_BTNFACE);
PatternBtnHighlight := GetSysColor(COLOR_BTNHIGHLIGHT);
Pattern := TBitmap.Create;
with Pattern do begin
Width := 8;
Height := 8;
with Canvas do begin
Brush.Style := bsSolid;
Brush.Color := clBtnFace;
FillRect (Rect(0, 0, Width, Height));
for Y := 0 to 7 do
for X := 0 to 7 do
if Odd(Y) = Odd(X) then { toggles between even/odd pixels }
Pixels[X, Y] := clBtnHighlight; { on even/odd rows }
end;
end;
end;
{ TButtonGlyph }
constructor TButtonGlyph.Create;
var
B: Boolean;
I: TButtonState97;
begin
inherited;
FCallDormant := True;
FImageIndex := -1;
FOriginal := TBitmap.Create;
FOriginal.OnChange := GlyphChanged;
FOriginalMask := TBitmap.Create;
FOriginalMask.OnChange := GlyphChanged;
FNumGlyphs := 1;
for B := False to True do
for I := Low(I) to High(I) do
FIndexs[B, I] := -1;
if GlyphCache = nil then
GlyphCache := TGlyphCache.Create;
end;
destructor TButtonGlyph.Destroy;
begin
FOriginalMask.Free;
FOriginal.Free;
FImageChangeLink.Free;
Invalidate;
if Assigned(GlyphCache) and GlyphCache.Empty then begin
GlyphCache.Free;
GlyphCache := nil;
end;
inherited;
end;
procedure TButtonGlyph.Invalidate;
var
B: Boolean;
I: TButtonState97;
begin
for B := False to True do begin
for I := Low(I) to High(I) do
if FIndexs[B, I] <> -1 then begin
FGlyphList[B].Delete (FIndexs[B, I]);
FIndexs[B, I] := -1;
end;
GlyphCache.ReturnList (FGlyphList[B]);
FGlyphList[B] := nil;
end;
end;
procedure TButtonGlyph.GlyphChanged (Sender: TObject);
begin
if (Sender = FOriginal) and (FOriginal.Width <> 0) and (FOriginal.Height <> 0) then
FTransparentColor := FOriginal.Canvas.Pixels[0, FOriginal.Height-1] or $02000000;
Invalidate;
if Assigned(FOnChange) then FOnChange (Self);
end;
procedure TButtonGlyph.UpdateNumGlyphs;
var
Glyphs: Integer;
begin
if (FOriginal.Width <> 0) and (FOriginal.Height <> 0) and
(FOriginal.Width mod FOriginal.Height = 0) then begin
Glyphs := FOriginal.Width div FOriginal.Height;
if Glyphs > High(TNumGlyphs97) then Glyphs := 1;
end
else
Glyphs := 1;
SetNumGlyphs (Glyphs);
end;
procedure TButtonGlyph.SetGlyph (Value: TBitmap);
begin
Invalidate;
FOriginal.Assign (Value);
UpdateNumGlyphs;
end;
procedure TButtonGlyph.SetGlyphMask (Value: TBitmap);
begin
Invalidate;
FOriginalMask.Assign (Value);
end;
procedure TButtonGlyph.SetNumGlyphs (Value: TNumGlyphs97);
begin
Invalidate;
if (FImageList <> nil) or (Value < Low(TNumGlyphs97)) or
(Value > High(TNumGlyphs97)) then
FNumGlyphs := 1
else
FNumGlyphs := Value;
GlyphChanged (nil);
end;
function TButtonGlyph.CreateButtonGlyph (State: TButtonState97): TBoolInt;
const
ROP_DSPDxax = $00E20746;
ROP_PSDPxax = $00B8074A;
ROP_DSna = $00220326; { D & ~S }
procedure GenerateMaskBitmapFromDIB (const MaskBitmap, SourceBitmap: TBitmap;
const SourceOffset, SourceSize: TPoint; TransColors: array of TColor);
{ This a special procedure meant for generating monochrome masks from
>4 bpp color DIB sections. Because each video driver seems to sport its own
interpretation of how to handle DIB sections, a workaround procedure like
this was necessary. }
type
TColorArray = array[0..536870910] of TColorRef;
var
Info: packed record
Header: TBitmapInfoHeader;
Colors: array[0..1] of TColorRef;
end;
W, H: Integer;
I, Y, X: Integer;
Pixels: ^TColorArray;
Pixel: ^TColorRef;
MonoPixels: Pointer;
MonoPixel, StartMonoPixel: ^Byte;
MonoScanLineSize, CurBit: Integer;
DC: HDC;
MaskBmp: HBITMAP;
begin
W := SourceBitmap.Width;
H := SourceBitmap.Height;
MonoScanLineSize := SourceSize.X div 8;
if SourceSize.X mod 8 <> 0 then
Inc (MonoScanLineSize);
if MonoScanLineSize mod 4 <> 0 then { Compensate for scan line boundary }
MonoScanLineSize := (MonoScanLineSize and not 3) + 4;
MonoPixels := AllocMem(MonoScanLineSize * SourceSize.Y); { AllocMem is used because it initializes to zero }
try
GetMem (Pixels, W * H * 4);
try
FillChar (Info, SizeOf(Info), 0);
with Info do begin
with Header do begin
biSize := SizeOf(TBitmapInfoHeader);
biWidth := W;
biHeight := -H; { negative number makes it a top-down DIB }
biPlanes := 1;
biBitCount := 32;
{biCompression := BI_RGB;} { implied due to the FillChar zeroing }
end;
{Colors[0] := clBlack;} { implied due to the FillChar zeroing }
Colors[1] := clWhite;
end;
DC := CreateCompatibleDC(0);
GetDIBits (DC, SourceBitmap.Handle, 0, H, Pixels, PBitmapInfo(@Info)^,
DIB_RGB_COLORS);
DeleteDC (DC);
for I := 0 to High(TransColors) do
if TransColors[I] = -1 then
TransColors[I] := Pixels[W * (H-1)] and $FFFFFF;
{ ^ 'and' operation is necessary because the high byte is undefined }
MonoPixel := MonoPixels;
for Y := SourceOffset.Y to SourceOffset.Y+SourceSize.Y-1 do begin
StartMonoPixel := MonoPixel;
CurBit := 7;
Pixel := @Pixels[(Y * W) + SourceOffset.X];
for X := 0 to SourceSize.X-1 do begin
for I := 0 to High(TransColors) do
if Pixel^ and $FFFFFF = Cardinal(TransColors[I]) then begin
{ ^ 'and' operation is necessary because the high byte is undefined }
MonoPixel^ := MonoPixel^ or (1 shl CurBit);
Break;
end;
Dec (CurBit);
if CurBit < 0 then begin
Inc (Integer(MonoPixel));
CurBit := 7;
end;
Inc (Integer(Pixel), SizeOf(Longint)); { proceed to the next pixel }
end;
Integer(MonoPixel) := Integer(StartMonoPixel) + MonoScanLineSize;
end;
finally
FreeMem (Pixels);
end;
{ Write new bits into a new HBITMAP, and assign this handle to MaskBitmap }
MaskBmp := CreateBitmap(SourceSize.X, SourceSize.Y, 1, 1, nil);
with Info.Header do begin
biWidth := SourceSize.X;
biHeight := -SourceSize.Y; { negative number makes it a top-down DIB }
biPlanes := 1;
biBitCount := 1;
end;
DC := CreateCompatibleDC(0);
SetDIBits (DC, MaskBmp, 0, SourceSize.Y, MonoPixels, PBitmapInfo(@Info)^,
DIB_RGB_COLORS);
DeleteDC (DC);
finally
FreeMem (MonoPixels);
end;
MaskBitmap.Handle := MaskBmp;
end;
procedure GenerateMaskBitmap (const MaskBitmap, SourceBitmap: TBitmap;
const SourceOffset, SourceSize: TPoint; const TransColors: array of TColor);
{ Returns handle of a monochrome bitmap, with pixels in SourceBitmap of color
TransColor set to white in the resulting bitmap. All other colors of
SourceBitmap are set to black in the resulting bitmap. This uses the
regular ROP_DSPDxax BitBlt method. }
var
CanvasHandle: HDC;
SaveBkColor: TColorRef;
DC: HDC;
MaskBmp, SaveBmp: HBITMAP;
I: Integer;
const
ROP: array[Boolean] of DWORD = (SRCPAINT, SRCCOPY);
begin
CanvasHandle := SourceBitmap.Canvas.Handle;
MaskBmp := CreateBitmap(SourceSize.X, SourceSize.Y, 1, 1, nil);
DC := CreateCompatibleDC(0);
SaveBmp := SelectObject(DC, MaskBmp);
SaveBkColor := GetBkColor(CanvasHandle);
for I := 0 to High(TransColors) do begin
SetBkColor (CanvasHandle, ColorToRGB(TransColors[I]));
BitBlt (DC, 0, 0, SourceSize.X, SourceSize.Y, CanvasHandle,
SourceOffset.X, SourceOffset.Y, ROP[I = 0]);
end;
SetBkColor (CanvasHandle, SaveBkColor);
SelectObject (DC, SaveBmp);
DeleteDC (DC);
MaskBitmap.Handle := MaskBmp;
end;
procedure ReplaceBitmapColorsFromMask (const MaskBitmap, DestBitmap: TBitmap;
const DestOffset, DestSize: TPoint; const ReplaceColor: TColor);
var
DestDC: HDC;
SaveBrush: HBRUSH;
SaveTextColor, SaveBkColor: TColorRef;
begin
DestDC := DestBitmap.Canvas.Handle;
SaveBrush := SelectObject(DestDC, CreateSolidBrush(ColorToRGB(ReplaceColor)));
SaveTextColor := SetTextColor(DestDC, clBlack);
SaveBkColor := SetBkColor(DestDC, clWhite);
BitBlt (DestDC, DestOffset.X, DestOffset.Y, DestSize.X, DestSize.Y,
MaskBitmap.Canvas.Handle, 0, 0, ROP_DSPDxax);
SetBkColor (DestDC, SaveBkColor);
SetTextColor (DestDC, SaveTextColor);
DeleteObject (SelectObject(DestDC, SaveBrush));
end;
function CopyBitmapToDDB (const SourceBitmap: TBitmap): TBitmap;
{ Makes a device-dependent duplicate of SourceBitmap. The color palette,
if any, is preserved. }
var
SB: HBITMAP;
SavePalette: HPALETTE;
DC: HDC;
BitmapInfo: packed record
Header: TBitmapInfoHeader;
Colors: array[0..255] of TColorRef;
end;
Bits: Pointer;
begin
Result := TBitmap.Create;
try
Result.Palette := CopyPalette(SourceBitmap.Palette);
Result.Width := SourceBitmap.Width;
Result.Height := SourceBitmap.Height;
SB := SourceBitmap.Handle;
if SB = 0 then Exit; { it would have a null handle if its width or height was zero }
SavePalette := 0;
DC := CreateCompatibleDC(0);
try
if Result.Palette <> 0 then begin
SavePalette := SelectPalette(DC, Result.Palette, False);
RealizePalette (DC);
end;
BitmapInfo.Header.biSize := SizeOf(TBitmapInfoHeader);
BitmapInfo.Header.biBitCount := 0; { instructs GetDIBits not to fill in the color table }
{ First retrieve the BitmapInfo header only }
if GetDIBits(DC, SB, 0, 0, nil, PBitmapInfo(@BitmapInfo)^, DIB_RGB_COLORS) <> 0 then begin
GetMem (Bits, BitmapInfo.Header.biSizeImage);
try
{ Then read the actual bits }
if GetDIBits(DC, SB, 0, SourceBitmap.Height, Bits, PBitmapInfo(@BitmapInfo)^, DIB_RGB_COLORS) <> 0 then
{ And copy them to the resulting bitmap }
SetDIBits (DC, Result.Handle, 0, SourceBitmap.Height, Bits, PBitmapInfo(@BitmapInfo)^, DIB_RGB_COLORS);
finally
FreeMem (Bits);
end;
end;
finally
if SavePalette <> 0 then SelectPalette (DC, SavePalette, False);
DeleteDC (DC);
end;
except
Result.Free;
raise;
end;
end;
const
ROPs: array[Boolean] of DWORD = (ROP_PSDPxax, ROP_DSPDxax);
var
OriginalBmp, OriginalMaskBmp, TmpImage, DDB, MonoBmp, MaskBmp, UseMaskBmp: TBitmap;
I: TButtonState97;
B: Boolean;
AddPixels, IWidth, IHeight, IWidthA, IHeightA: Integer;
IRect, IRectA, SourceRect, R: TRect;
DC: HDC;
UsesMask: Boolean;
{$IFDEF TB97D3}
IsHighColorDIB: Boolean;
{$ELSE}
const
IsHighColorDIB = False;
{$ENDIF}
begin
if (State <> bsDisabled) and (Ord(State) >= NumGlyphs) then
State := bsUp;
Result.B := True;
Result.I := FIndexs[True, State];
if Result.I = -1 then begin
Result.B := False;
Result.I := FIndexs[False, State];
end;
if Result.I <> -1 then Exit;
if FImageList = nil then begin
if (FOriginal.Width = 0) or (FOriginal.Height = 0) then Exit;
UsesMask := (FOriginalMask.Width <> 0) and (FOriginalMask.Height <> 0);
end
else begin
if (FImageIndex < 0) or (FImageIndex >= FImageList.Count) then Exit;
UsesMask := False;
end;
B := State <> bsDisabled;
{ + AddPixels is to make sure the highlight color on generated disabled glyphs
doesn't get cut off }
if FImageList = nil then begin
IWidthA := FOriginal.Width div FNumGlyphs;
IHeightA := FOriginal.Height;
end
else begin
IWidthA := TCustomImageListAccess(FImageList).Width;
IHeightA := TCustomImageListAccess(FImageList).Height;
end;
IRectA := Rect(0, 0, IWidthA, IHeightA);
AddPixels := Ord(State = bsDisabled);
IWidth := IWidthA + AddPixels;
IHeight := IHeightA + AddPixels;
IRect := Rect(0, 0, IWidth, IHeight);
if FGlyphList[B] = nil then begin
if GlyphCache = nil then
GlyphCache := TGlyphCache.Create;
FGlyphList[B] := GlyphCache.GetList(IWidth, IHeight);
end;
{$IFDEF TB97D3}
IsHighColorDIB := (FImageList = nil) and (FOriginal.PixelFormat > pf4bit);
{$ENDIF}
OriginalBmp := nil;
OriginalMaskBmp := nil;
TmpImage := nil;
MaskBmp := nil;
try
OriginalBmp := TBitmap.Create;
OriginalBmp.Assign (FOriginal);
OriginalMaskBmp := TBitmap.Create;
OriginalMaskBmp.Assign (FOriginalMask);
TmpImage := TBitmap.Create;
TmpImage.Width := IWidth;
TmpImage.Height := IHeight;
TmpImage.Canvas.Brush.Color := clBtnFace;
if FImageList = nil then
TmpImage.Palette := CopyPalette(OriginalBmp.Palette);
I := State;
if Ord(I) >= NumGlyphs then I := bsUp;
SourceRect := Bounds(Ord(I) * IWidthA, 0, IWidthA, IHeightA);
if FImageList <> nil then begin
MaskBmp := TBitmap.Create;
MaskBmp.Monochrome := True;
MaskBmp.Width := IWidthA;
MaskBmp.Height := IHeightA;
ImageList_Draw (FImageList.Handle, FImageIndex, MaskBmp.Canvas.Handle,
0, 0, ILD_MASK);
end;
if State <> bsDisabled then begin
if FImageList = nil then begin
TmpImage.Canvas.CopyRect (IRectA, OriginalBmp.Canvas, SourceRect);
if not UsesMask then begin
{$IFDEF TB97D3}
{ Use clDefault instead of FTransparentColor whereever possible to
ensure compatibility with all video drivers when using high-color
(> 4 bpp) DIB glyphs }
FIndexs[B, State] := FGlyphList[B].AddMasked(TmpImage, clDefault);
{$ELSE}
FIndexs[B, State] := FGlyphList[B].AddMasked(TmpImage, FTransparentColor);
{$ENDIF}
end
else begin
MonoBmp := TBitmap.Create;
try
MonoBmp.Monochrome := True;
MonoBmp.Width := IWidth;
MonoBmp.Height := IHeight;
MonoBmp.Canvas.CopyRect (IRectA, OriginalMaskBmp.Canvas, SourceRect);
FIndexs[B, State] := FGlyphList[B].Add(TmpImage, MonoBmp);
finally
MonoBmp.Free;
end;
end;
end
else begin
ImageList_Draw (FImageList.Handle, FImageIndex, TmpImage.Canvas.Handle,
0, 0, ILD_NORMAL);
FIndexs[B, State] := FGlyphList[B].Add(TmpImage, MaskBmp);
end;
end
else begin
MonoBmp := nil;
DDB := nil;
try
MonoBmp := TBitmap.Create;
{ Uses the CopyBitmapToDDB to work around a Delphi 3 flaw. If you copy
a DIB to a second bitmap via Assign, change the HandleType of the
second bitmap to bmDDB, then try to read the Handle property, Delphi
converts it back to a DIB. }
if FImageList = nil then
DDB := CopyBitmapToDDB(OriginalBmp)
else begin
DDB := TBitmap.Create;
DDB.Width := IWidthA;
DDB.Height := IHeightA;
ImageList_Draw (FImageList.Handle, FImageIndex, DDB.Canvas.Handle,
0, 0, ILD_NORMAL);
end;
if NumGlyphs > 1 then
with TmpImage.Canvas do begin
CopyRect (IRectA, DDB.Canvas, SourceRect);
{ Convert white to clBtnHighlight }
if not IsHighColorDIB then
GenerateMaskBitmap (MonoBmp, DDB, SourceRect.TopLeft,
IRectA.BottomRight, [GetNearestColor(OriginalBmp.Canvas.Handle, clWhite)])
else
GenerateMaskBitmapFromDIB (MonoBmp, OriginalBmp, SourceRect.TopLeft,
IRectA.BottomRight, [clWhite]);
ReplaceBitmapColorsFromMask (MonoBmp, TmpImage, IRectA.TopLeft,
IRectA.BottomRight, clBtnHighlight);
{ Convert gray to clBtnShadow }
if not IsHighColorDIB then
GenerateMaskBitmap (MonoBmp, DDB, SourceRect.TopLeft,
IRectA.BottomRight, [GetNearestColor(OriginalBmp.Canvas.Handle, clGray)])
else
GenerateMaskBitmapFromDIB (MonoBmp, OriginalBmp, SourceRect.TopLeft,
IRectA.BottomRight, [clGray]);
ReplaceBitmapColorsFromMask (MonoBmp, TmpImage, IRectA.TopLeft,
IRectA.BottomRight, clBtnShadow);
if not UsesMask then begin
{ Generate the transparent mask in MonoBmp. The reason why
it doesn't just use a mask color is because the mask needs
to be of the glyph -before- the clBtnHighlight/Shadow were
translated }
if not IsHighColorDIB then
GenerateMaskBitmap (MonoBmp, DDB,
SourceRect.TopLeft, IRectA.BottomRight, FTransparentColor)
else
GenerateMaskBitmapFromDIB (MonoBmp, OriginalBmp,
SourceRect.TopLeft, IRectA.BottomRight, [-1]);
end
else
MonoBmp.Canvas.CopyRect (IRectA, OriginalMaskBmp.Canvas, SourceRect);
with MonoBmp do begin
Width := Width + AddPixels;
Height := Height + AddPixels;
{ Set the additional bottom and right row on disabled glyph
masks to white so that it always shines through, since the
bottom and right row on TmpImage was left uninitialized }
Canvas.Pen.Color := clWhite;
Canvas.PolyLine ([Point(0, Height-1), Point(Width-1, Height-1),
Point(Width-1, -1)]);
end;
FIndexs[B, State] := FGlyphList[B].Add(TmpImage, MonoBmp);
end
else begin
{ Create a disabled version }
if FOldDisabledStyle then begin
{ "Old" TSpeedButton style }
if FImageList = nil then begin
if not UsesMask then begin
if IsHighColorDIB then
GenerateMaskBitmapFromDIB (MonoBmp, OriginalBmp,
SourceRect.TopLeft, IRectA.BottomRight, [clBlack])
else begin
with MonoBmp do begin
Assign (DDB); { must be a DDB for this to work right }
Canvas.Brush.Color := clBlack;
Monochrome := True;
end;
end;
end
else begin
MonoBmp.Assign (DDB); { must be a DDB for this to work right }
with TBitmap.Create do
try
Monochrome := True;
Width := OriginalMaskBmp.Width;
Height := OriginalMaskBmp.Height;
R := Rect(0, 0, Width, Height);
Canvas.CopyRect (R, OriginalMaskBmp.Canvas, R);
DC := Canvas.Handle;
with MonoBmp.Canvas do begin
BitBlt (Handle, 0, 0, IWidthA, IHeightA, DC,
SourceRect.Left, SourceRect.Top, ROP_DSna);
BitBlt (Handle, 0, 0, IWidthA, IHeightA, DC,
SourceRect.Left, SourceRect.Top, SRCPAINT);
end;
finally
Free;
end;
MonoBmp.Canvas.Brush.Color := clBlack;
MonoBmp.Monochrome := True;
end
end
else begin
with MonoBmp do begin
Width := IWidthA;
Height := IHeightA;
Canvas.Brush.Color := clWhite;
Canvas.FillRect (IRectA);
ImageList_Draw (FImageList.Handle, FImageIndex, Canvas.Handle,
0, 0, ILD_TRANSPARENT);
Canvas.Brush.Color := clBlack;
Monochrome := True;
end;
end;
end
else begin
{ The new Office 97 / MFC look }
if not UsesMask and (FImageList = nil) then begin
with TmpImage.Canvas do begin
if not IsHighColorDIB then
GenerateMaskBitmap (MonoBmp, DDB, IRectA.TopLeft,
IRectA.BottomRight, [FTransparentColor, clWhite, clSilver])
else
GenerateMaskBitmapFromDIB (MonoBmp, OriginalBmp,
SourceRect.TopLeft, IRectA.BottomRight, [-1, clWhite, clSilver]);
end;
end
else begin
{ Generate the mask in MonoBmp. Make clWhite and clSilver transparent. }
if not IsHighColorDIB then
GenerateMaskBitmap (MonoBmp, DDB, SourceRect.TopLeft,
IRectA.BottomRight, [clWhite, clSilver])
else
GenerateMaskBitmapFromDIB (MonoBmp, OriginalBmp, SourceRect.TopLeft,
IRectA.BottomRight, [clWhite, clSilver]);
if FImageList = nil then
UseMaskBmp := OriginalMaskBmp
else
UseMaskBmp := MaskBmp;
{ and all the white colors in UseMaskBmp }
with TBitmap.Create do
try
Monochrome := True;
Width := UseMaskBmp.Width;
Height := UseMaskBmp.Height;
R := Rect(0, 0, Width, Height);
Canvas.CopyRect (R, UseMaskBmp.Canvas, R);
DC := Canvas.Handle;
with MonoBmp.Canvas do begin
BitBlt (Handle, 0, 0, IWidthA, IHeightA, DC,
SourceRect.Left, SourceRect.Top, ROP_DSna);
BitBlt (Handle, 0, 0, IWidthA, IHeightA, DC,
SourceRect.Left, SourceRect.Top, SRCPAINT);
end;
finally
Free;
end;
end;
end;
with TmpImage.Canvas do begin
Brush.Color := clBtnFace;
FillRect (IRect);
Brush.Color := clBtnHighlight;
DC := Handle;
SetTextColor (DC, clBlack);
SetBkColor (DC, clWhite);
BitBlt (DC, 1, 1, IWidthA, IHeightA,
MonoBmp.Canvas.Handle, 0, 0, ROPs[FOldDisabledStyle]);
Brush.Color := clBtnShadow;
DC := Handle;
SetTextColor (DC, clBlack);
SetBkColor (DC, clWhite);
BitBlt (DC, 0, 0, IWidthA, IHeightA,
MonoBmp.Canvas.Handle, 0, 0, ROPs[FOldDisabledStyle]);
end;
FIndexs[B, State] := FGlyphList[B].AddMasked(TmpImage, clBtnFace);
end;
finally
DDB.Free;
MonoBmp.Free;
end;
end;
finally
MaskBmp.Free;
TmpImage.Free;
OriginalMaskBmp.Free;
OriginalBmp.Free;
end;
Result.B := B;
Result.I := FIndexs[B, State];
{ Note: Due to a bug in graphics.pas, Delphi 2's VCL crashes if Dormant is
called on an empty bitmap, so to prevent this it must check Width/Height
first }
if {$IFNDEF TB97D3} (FOriginal.Width <> 0) and (FOriginal.Height <> 0) and {$ENDIF}
FCallDormant then
FOriginal.Dormant;
{$IFNDEF TB97D3} if (FOriginalMask.Width <> 0) and (FOriginalMask.Height <> 0) then {$ENDIF}
FOriginalMask.Dormant;
end;
procedure TButtonGlyph.DrawButtonGlyph (Canvas: TCanvas; const GlyphPos: TPoint;
State: TButtonState97);
var
Index: TBoolInt;
begin
Index := CreateButtonGlyph(State);
if Index.I <> -1 then
ImageList_DrawEx (FGlyphList[Index.B].Handle, Index.I, Canvas.Handle,
GlyphPos.X, GlyphPos.Y, 0, 0, CLR_NONE, CLR_NONE, ILD_TRANSPARENT);
end;
procedure TButtonGlyph.DrawButtonText (Canvas: TCanvas; const Caption: string;
TextBounds: TRect; WordWrap: Boolean; Alignment: TAlignment;
State: TButtonState97);
const
AlignmentFlags: array[TAlignment] of UINT = (DT_LEFT, DT_RIGHT, DT_CENTER);
var
Format: UINT;
begin
Format := DT_VCENTER or AlignmentFlags[Alignment];
if not WordWrap then
Format := Format or DT_SINGLELINE
else
Format := Format or DT_WORDBREAK;
with Canvas do begin
Brush.Style := bsClear;
if State = bsDisabled then begin
OffsetRect (TextBounds, 1, 1);
Font.Color := clBtnHighlight;
DrawText (Handle, PChar(Caption), Length(Caption), TextBounds, Format);
OffsetRect (TextBounds, -1, -1);
Font.Color := clBtnShadow;
DrawText (Handle, PChar(Caption), Length(Caption), TextBounds, Format);
end
else
DrawText (Handle, PChar(Caption), Length(Caption), TextBounds, Format);
end;
end;
procedure TButtonGlyph.DrawButtonDropArrow (Canvas: TCanvas;
const X, Y, AWidth: Integer; State: TButtonState97);
var
X2: Integer;
begin
with Canvas do begin
X2 := X + AWidth div 2;
if State = bsDisabled then begin
Pen.Color := clBtnHighlight;
Brush.Color := clBtnHighlight;
Polygon ([Point(X2-1, Y+1), Point(X2+3, Y+1), Point(X2+1, Y+3)]);
Pen.Color := clBtnShadow;
Brush.Color := clBtnShadow;
Polygon ([Point(X2-2, Y), Point(X2+2, Y), Point(X2, Y+2)]);
end
else begin
Pen.Color := Font.Color;
Brush.Color := Font.Color;
Polygon ([Point(X2-2, Y), Point(X2+2, Y), Point(X2, Y+2)]);
end;
end;
end;
procedure TButtonGlyph.CalcButtonLayout (Canvas: TCanvas; const Client: TRect;
const Offset: TPoint; DrawGlyph, DrawCaption: Boolean; const Caption: string;
WordWrap: Boolean; Layout: TButtonLayout; Margin, Spacing: Integer;
DropArrow: Boolean; DropArrowWidth: Integer; var GlyphPos, ArrowPos: TPoint;
var TextBounds: TRect);
var
TextPos: TPoint;
ClientSize, GlyphSize, TextSize, ArrowSize: TPoint;
HasGlyph: Boolean;
TotalSize: TPoint;
Format: UINT;
Margin1, Spacing1: Integer;
LayoutLeftOrRight: Boolean;
begin
{ calculate the item sizes }
ClientSize := Point(Client.Right-Client.Left, Client.Bottom-Client.Top);
GlyphSize.X := 0;
GlyphSize.Y := 0;
if DrawGlyph then begin
if FImageList = nil then begin
if FOriginal <> nil then begin
GlyphSize.X := FOriginal.Width div FNumGlyphs;
GlyphSize.Y := FOriginal.Height;
end;
end
else begin
GlyphSize.X := TCustomImageListAccess(FImageList).Width;
GlyphSize.Y := TCustomImageListAccess(FImageList).Height;
end;
end;
HasGlyph := (GlyphSize.X <> 0) and (GlyphSize.Y <> 0);
if DropArrow then begin
ArrowSize.X := DropArrowWidth;
ArrowSize.Y := 3;
end
else begin
ArrowSize.X := 0;
ArrowSize.Y := 0;
end;
LayoutLeftOrRight := Layout in [blGlyphLeft, blGlyphRight];
if not LayoutLeftOrRight and not HasGlyph then begin
Layout := blGlyphLeft;
LayoutLeftOrRight := True;
end;
if DrawCaption and (Caption <> '') then begin
TextBounds := Rect(0, 0, Client.Right-Client.Left, 0);
if LayoutLeftOrRight then
Dec (TextBounds.Right, ArrowSize.X);
Format := DT_CALCRECT;
if WordWrap then begin
Format := Format or DT_WORDBREAK;
Margin1 := 4;
if LayoutLeftOrRight and HasGlyph then begin
if Spacing = -1 then
Spacing1 := 4
else
Spacing1 := Spacing;
Dec (TextBounds.Right, GlyphSize.X + Spacing1);
if Margin <> -1 then
Margin1 := Margin
else
if Spacing <> -1 then
Margin1 := Spacing;
end;
Dec (TextBounds.Right, Margin1 * 2);
end;
DrawText (Canvas.Handle, PChar(Caption), Length(Caption), TextBounds, Format);
TextSize := Point(TextBounds.Right - TextBounds.Left, TextBounds.Bottom -
TextBounds.Top);
end
else begin
TextBounds := Rect(0, 0, 0, 0);
TextSize := Point(0,0);
end;
{ If the layout has the glyph on the right or the left, then both the
text and the glyph are centered vertically. If the glyph is on the top
or the bottom, then both the text and the glyph are centered horizontally.}
if LayoutLeftOrRight then begin
GlyphPos.Y := (ClientSize.Y - GlyphSize.Y + 1) div 2;
TextPos.Y := (ClientSize.Y - TextSize.Y + 1) div 2;
end
else begin
GlyphPos.X := (ClientSize.X - GlyphSize.X - ArrowSize.X + 1) div 2;
TextPos.X := (ClientSize.X - TextSize.X + 1) div 2;
if not HasGlyph then
ArrowPos.X := TextPos.X + TextSize.X
else
ArrowPos.X := GlyphPos.X + GlyphSize.X;
end;
{ if there is no text or no bitmap, then Spacing is irrelevant }
if (TextSize.X = 0) or (TextSize.Y = 0) or not HasGlyph then
Spacing := 0;
{ adjust Margin and Spacing }
if Margin = -1 then begin
if Spacing = -1 then begin
TotalSize := Point(GlyphSize.X + TextSize.X + ArrowSize.X,
GlyphSize.Y + TextSize.Y);
if LayoutLeftOrRight then
Margin := (ClientSize.X - TotalSize.X) div 3
else
Margin := (ClientSize.Y - TotalSize.Y) div 3;
Spacing := Margin;
end
else begin
TotalSize := Point(GlyphSize.X + Spacing + TextSize.X + ArrowSize.X,
GlyphSize.Y + Spacing + TextSize.Y);
if LayoutLeftOrRight then
Margin := (ClientSize.X - TotalSize.X + 1) div 2
else
Margin := (ClientSize.Y - TotalSize.Y + 1) div 2;
end;
end
else begin
if Spacing = -1 then begin
TotalSize := Point(ClientSize.X - (Margin + GlyphSize.X + ArrowSize.X),
ClientSize.Y - (Margin + GlyphSize.Y));
if LayoutLeftOrRight then
Spacing := (TotalSize.X - TextSize.X) div 2
else
Spacing := (TotalSize.Y - TextSize.Y) div 2;
end;
end;
case Layout of
blGlyphLeft: begin
GlyphPos.X := Margin;
TextPos.X := GlyphPos.X + GlyphSize.X + Spacing;
ArrowPos.X := TextPos.X + TextSize.X;
end;
blGlyphRight: begin
ArrowPos.X := ClientSize.X - Margin - ArrowSize.X;
GlyphPos.X := ArrowPos.X - GlyphSize.X;
TextPos.X := GlyphPos.X - Spacing - TextSize.X;
end;
blGlyphTop: begin
GlyphPos.Y := Margin;
TextPos.Y := GlyphPos.Y + GlyphSize.Y + Spacing;
end;
blGlyphBottom: begin
GlyphPos.Y := ClientSize.Y - Margin - GlyphSize.Y;
TextPos.Y := GlyphPos.Y - Spacing - TextSize.Y;
end;
end;
Inc (ArrowPos.X);
if not HasGlyph then
ArrowPos.Y := TextPos.Y + (TextSize.Y - ArrowSize.Y) div 2
else
ArrowPos.Y := GlyphPos.Y + (GlyphSize.Y - ArrowSize.Y) div 2;
{ fixup the result variables }
with GlyphPos do begin
Inc (X, Client.Left + Offset.X);
Inc (Y, Client.Top + Offset.Y);
end;
with ArrowPos do begin
Inc (X, Client.Left + Offset.X);
Inc (Y, Client.Top + Offset.Y);
end;
OffsetRect (TextBounds, TextPos.X + Client.Left + Offset.X,
TextPos.Y + Client.Top + Offset.X);
end;
function TButtonGlyph.Draw (Canvas: TCanvas; const Client: TRect;
const Offset: TPoint; DrawGlyph, DrawCaption: Boolean; const Caption: string;
WordWrap: Boolean; Alignment: TAlignment; Layout: TButtonLayout;
Margin, Spacing: Integer; DropArrow: Boolean; DropArrowWidth: Integer;
State: TButtonState97): TRect;
var
GlyphPos, ArrowPos: TPoint;
begin
CalcButtonLayout (Canvas, Client, Offset, DrawGlyph, DrawCaption, Caption,
WordWrap, Layout, Margin, Spacing, DropArrow, DropArrowWidth, GlyphPos,
ArrowPos, Result);
if DrawGlyph then
DrawButtonGlyph (Canvas, GlyphPos, State);
if DrawCaption then
DrawButtonText (Canvas, Caption, Result, WordWrap, Alignment, State);
if DropArrow then
DrawButtonDropArrow (Canvas, ArrowPos.X, ArrowPos.Y, DropArrowWidth, State);
end;
{ TDropdownList }
{$IFNDEF TB97D4}
type
TDropdownList = class(TComponent)
private
List: TList;
Window: HWND;
procedure WndProc (var Message: TMessage);
protected
procedure Notification (AComponent: TComponent; Operation: TOperation); override;
public
constructor Create (AOwner: TComponent); override;
destructor Destroy; override;
procedure AddMenu (Menu: TPopupMenu);
end;
var
DropdownList: TDropdownList;
constructor TDropdownList.Create (AOwner: TComponent);
begin
inherited;
List := TList.Create;
end;
destructor TDropdownList.Destroy;
begin
inherited;
if Window <> 0 then
DeallocateHWnd (Window);
List.Free;
end;
procedure TDropdownList.WndProc (var Message: TMessage);
{ This procedure is based on code from TPopupList.WndProc (menus.pas) }
var
I: Integer;
MenuItem: TMenuItem;
FindKind: TFindItemKind;
ContextID: Integer;
begin
try
with List do
case Message.Msg of
WM_COMMAND:
for I := 0 to Count-1 do
if TPopupMenu(Items[I]).DispatchCommand(TWMCommand(Message).ItemID) then
Exit;
WM_INITMENUPOPUP:
for I := 0 to Count-1 do
if TPopupMenu(Items[I]).DispatchPopup(TWMInitMenuPopup(Message).MenuPopup) then
Exit;
WM_MENUSELECT:
with TWMMenuSelect(Message) do begin
FindKind := fkCommand;
if MenuFlag and MF_POPUP <> 0 then
FindKind := fkHandle;
for I := 0 to Count-1 do begin
MenuItem := TPopupMenu(Items[I]).FindItem(IDItem, FindKind);
if MenuItem <> nil then begin
Application.Hint := MenuItem.Hint;
Exit;
end;
end;
Application.Hint := '';
end;
WM_HELP:
with TWMHelp(Message).HelpInfo^ do begin
for I := 0 to Count-1 do
if TPopupMenu(Items[I]).Handle = hItemHandle then begin
ContextID := TPopupMenu(Items[I]).GetHelpContext(iCtrlID, True);
if ContextID = 0 then
ContextID := TPopupMenu(Items[I]).GetHelpContext(hItemHandle, False);
if Screen.ActiveForm = nil then Exit;
if (biHelp in Screen.ActiveForm.BorderIcons) then
Application.HelpCommand (HELP_CONTEXTPOPUP, ContextID)
else
Application.HelpContext (ContextID);
Exit;
end;
end;
end;
with Message do
Result := DefWindowProc(Window, Msg, wParam, lParam);
except
Application.HandleException (Self);
end;
end;
procedure TDropdownList.AddMenu (Menu: TPopupMenu);
begin
if List.IndexOf(Menu) = -1 then begin
if Window = 0 then
Window := AllocateHWnd(WndProc);
Menu.FreeNotification (Self);
List.Add (Menu);
end;
end;
procedure TDropdownList.Notification (AComponent: TComponent; Operation: TOperation);
begin
inherited;
if Operation = opRemove then begin
List.Remove (AComponent);
if (List.Count = 0) and (Window <> 0) then begin
DeallocateHWnd (Window);
Window := 0;
end;
end;
end;
{$ENDIF}
{ TToolbarButton97 }
procedure ButtonHookProc (Code: THookProcCode; Wnd: HWND; WParam: WPARAM; LParam: LPARAM);
var
P: TPoint;
begin
case Code of
hpSendActivateApp:
if (WParam = 0) and Assigned(ButtonMouseInControl) and
not ButtonMouseInControl.FShowBorderWhenInactive then
ButtonMouseInControl.MouseLeft;
hpPostMouseMove: begin
if Assigned(ButtonMouseInControl) then begin
GetCursorPos (P);
if FindDragTarget(P, True) <> ButtonMouseInControl then
ButtonMouseInControl.MouseLeft;
end;
end;
end;
end;
constructor TToolbarButton97.Create (AOwner: TComponent);
begin
inherited;
if ButtonMouseTimer = nil then begin
ButtonMouseTimer := TTimer.Create(nil);
ButtonMouseTimer.Enabled := False;
ButtonMouseTimer.Interval := 125; { 8 times a second }
end;
InstallHookProc (ButtonHookProc, [hpSendActivateApp, hpPostMouseMove],
csDesigning in ComponentState);
SetBounds (Left, Top, 23, 22);
ControlStyle := [csCaptureMouse, csDoubleClicks, csOpaque];
Color := clBtnFace;
FGlyph := TButtonGlyph.Create;
TButtonGlyph(FGlyph).OnChange := GlyphChanged;
ParentFont := True;
FAlignment := taCenter;
FFlat := True;
FHighlightWhenDown := True;
FOpaque := True;
FSpacing := 4;
FMargin := -1;
FLayout := blGlyphLeft;
FDropdownArrow := True;
FDropdownArrowWidth := DefaultDropdownArrowWidth;
FRepeatDelay := 400;
FRepeatInterval := 100;
Inc (ButtonCount);
end;
destructor TToolbarButton97.Destroy;
begin
RemoveButtonMouseTimer;
TButtonGlyph(FGlyph).Free;
{ The Notification method, which is sometimes called while the component is
being destroyed, reads FGlyph and expects it to be valid, so it must be
reset to nil }
FGlyph := nil;
UninstallHookProc (ButtonHookProc);
Dec (ButtonCount);
if ButtonCount = 0 then begin
Pattern.Free;
Pattern := nil;
ButtonMouseTimer.Free;
ButtonMouseTimer := nil;
end;
inherited;
end;
procedure TToolbarButton97.Paint;
const
EdgeStyles: array[Boolean, Boolean] of UINT = (
(EDGE_RAISED, EDGE_SUNKEN),
(BDR_RAISEDINNER, BDR_SUNKENOUTER));
FlagStyles: array[Boolean] of UINT = (BF_RECT or BF_SOFT or BF_MIDDLE, BF_RECT);
var
UseBmp: Boolean;
Bmp: TBitmap;
DrawCanvas: TCanvas;
PaintRect, R: TRect;
Offset: TPoint;
StateDownOrExclusive, DropdownComboShown, UseDownAppearance, DrawBorder: Boolean;
begin
UseBmp := FOpaque or not FFlat;
if UseBmp then
Bmp := TBitmap.Create
else
Bmp := nil;
try
if UseBmp then begin
Bmp.Width := Width;
Bmp.Height := Height;
DrawCanvas := Bmp.Canvas;
with DrawCanvas do begin
Brush.Color := Color;
FillRect (ClientRect);
end;
end
else
DrawCanvas := Canvas;
DrawCanvas.Font := Self.Font;
PaintRect := Rect(0, 0, Width, Height);
StateDownOrExclusive := FState in [bsDown, bsExclusive];
DropdownComboShown := FDropdownCombo and FUsesDropdown;
UseDownAppearance := (FState = bsExclusive) or
((FState = bsDown) and (not DropdownComboShown or not FMenuIsDown));
DrawBorder := (csDesigning in ComponentState) or
(not FNoBorder and (not FFlat or StateDownOrExclusive or (FMouseInControl and (FState <> bsDisabled))));
if DropdownComboShown then begin
if DrawBorder then begin
R := PaintRect;
Dec (R.Right, DropdownComboSpace);
R.Left := R.Right - DropdownArrowWidth;
DrawEdge (DrawCanvas.Handle, R,
EdgeStyles[FFlat, StateDownOrExclusive and FMenuIsDown],
FlagStyles[FFlat]);
end;
Dec (PaintRect.Right, DropdownArrowWidth + DropdownComboSpace);
end;
if DrawBorder then
DrawEdge (DrawCanvas.Handle, PaintRect, EdgeStyles[FFlat, UseDownAppearance],
FlagStyles[FFlat]);
if not FNoBorder then begin
if FFlat then
InflateRect (PaintRect, -1, -1)
else
InflateRect (PaintRect, -2, -2);
end;
if UseDownAppearance then begin
if (FState = bsExclusive) and (not FFlat or not FMouseInControl) and
not FMenuIsDown and FHighlightWhenDown then begin
if Pattern = nil then CreateBrushPattern;
DrawCanvas.Brush.Bitmap := Pattern;
DrawCanvas.FillRect(PaintRect);
end;
Offset.X := 1;
Offset.Y := 1;
end
else begin
Offset.X := 0;
Offset.Y := 0;
end;
TButtonGlyph(FGlyph).Draw (DrawCanvas, PaintRect, Offset,
FDisplayMode <> dmTextOnly, FDisplayMode <> dmGlyphOnly,
Caption, FWordWrap, FAlignment, FLayout, FMargin, FSpacing,
FDropdownArrow and not FDropdownCombo and FUsesDropdown,
DropdownArrowWidth, FState);
if DropdownComboShown then
TButtonGlyph(FGlyph).DrawButtonDropArrow (DrawCanvas, Width-DropdownArrowWidth-2,
Height div 2 - 1, DropdownArrowWidth, FState);
if UseBmp then
Canvas.Draw (0, 0, Bmp);
finally
Bmp.Free;
end;
end;
procedure TToolbarButton97.RemoveButtonMouseTimer;
begin
if ButtonMouseInControl = Self then begin
ButtonMouseTimer.Enabled := False;
ButtonMouseInControl := nil;
end;
end;
(* no longer used
procedure TToolbarButton97.UpdateTracking;
var
P: TPoint;
begin
if Enabled then begin
GetCursorPos (P);
{ Use FindDragTarget instead of PtInRect since we want to check based on
the Z order }
FMouseInControl := not (FindDragTarget(P, True) = Self);
if FMouseInControl then
MouseLeft
else
MouseEntered;
end;
end;
*)
procedure TToolbarButton97.Loaded;
var
State: TButtonState97;
begin
inherited;
if Enabled then
State := bsUp
else
State := bsDisabled;
TButtonGlyph(FGlyph).CreateButtonGlyph (State);
end;
procedure TToolbarButton97.Notification (AComponent: TComponent; Operation: TOperation);
begin
inherited;
if Operation = opRemove then begin
if AComponent = DropdownMenu then DropdownMenu := nil;
if Assigned(FGlyph) and (AComponent = Images) then Images := nil;
end;
end;
function TToolbarButton97.PointInButton (X, Y: Integer): Boolean;
begin
Result := (X >= 0) and
(X < ClientWidth-((DropdownArrowWidth+DropdownComboSpace) * Ord(FDropdownCombo and FUsesDropdown))) and
(Y >= 0) and (Y < ClientHeight);
end;
procedure TToolbarButton97.MouseDown (Button: TMouseButton; Shift: TShiftState;
X, Y: Integer);
begin
if not Enabled then begin
inherited;
Exit;
end;
if Button <> mbLeft then begin
MouseEntered;
inherited;
end
else begin
{ We know mouse has to be over the control if the mouse went down. }
MouseEntered;
FMenuIsDown := FUsesDropdown and (not FDropdownCombo or
(X >= Width-(DropdownArrowWidth+DropdownComboSpace)));
try
if not FDown then begin
FState := bsDown;
Redraw (True);
end
else
if FAllowAllUp then
Redraw (True);
if not FMenuIsDown then
FMouseIsDown := True;
inherited;
if FMenuIsDown then
Click
else
if FRepeating then begin
Click;
if not Assigned(FRepeatTimer) then
FRepeatTimer := TTimer.Create(Self);
FRepeatTimer.Enabled := False;
FRepeatTimer.Interval := FRepeatDelay;
FRepeatTimer.OnTimer := RepeatTimerHandler;
FRepeatTimer.Enabled := True;
end;
finally
FMenuIsDown := False;
end;
end;
end;
procedure TToolbarButton97.MouseMove (Shift: TShiftState; X, Y: Integer);
var
P: TPoint;
NewState: TButtonState97;
PtInButton: Boolean;
begin
inherited;
{ Check if mouse just entered the control. It works better to check this
in MouseMove rather than using CM_MOUSEENTER, since the VCL doesn't send
a CM_MOUSEENTER in all cases
Use FindDragTarget instead of PtInRect since we want to check based on
the Z order }
P := ClientToScreen(Point(X, Y));
if (ButtonMouseInControl <> Self) and (FindDragTarget(P, True) = Self) then begin
if Assigned(ButtonMouseInControl) then
ButtonMouseInControl.MouseLeft;
{ Like Office 97, only draw the active borders when the application is active }
if FShowBorderWhenInactive or ApplicationIsActive then begin
ButtonMouseInControl := Self;
ButtonMouseTimer.OnTimer := ButtonMouseTimerHandler;
ButtonMouseTimer.Enabled := True;
MouseEntered;
end;
end;
if FMouseIsDown then begin
PtInButton := PointInButton(X, Y);
if PtInButton and Assigned(FRepeatTimer) then
FRepeatTimer.Enabled := True;
if FDown then
NewState := bsExclusive
else begin
if PtInButton then
NewState := bsDown
else
NewState := bsUp;
end;
if NewState <> FState then begin
FState := NewState;
Redraw (True);
end;
end;
end;
procedure TToolbarButton97.RepeatTimerHandler (Sender: TObject);
var
P: TPoint;
begin
FRepeatTimer.Interval := FRepeatInterval;
GetCursorPos (P);
P := ScreenToClient(P);
if Repeating and FMouseIsDown and MouseCapture and PointInButton(P.X, P.Y) then
Click
else
FRepeatTimer.Enabled := False;
end;
procedure TToolbarButton97.WMCancelMode (var Message: TWMCancelMode);
begin
FRepeatTimer.Free;
FRepeatTimer := nil;
if FMouseIsDown then begin
FMouseIsDown := False;
MouseLeft;
end;
{ Delphi's default processing of WM_CANCELMODE sends a "fake" WM_LBUTTONUP
message to the control, so inherited must only be called after setting
FMouseIsDown to False }
inherited;
end;
procedure TToolbarButton97.MouseUp (Button: TMouseButton; Shift: TShiftState;
X, Y: Integer);
begin
FRepeatTimer.Free;
FRepeatTimer := nil;
{ Remove active border when right button is clicked }
if (Button = mbRight) and Enabled then begin
FMouseIsDown := False;
MouseLeft;
end;
inherited;
if (Button = mbLeft) and FMouseIsDown then begin
FMouseIsDown := False;
if PointInButton(X, Y) and not FRepeating then
Click
else
MouseLeft;
end;
end;
procedure TToolbarButton97.Click;
{$IFNDEF TB97D4}
const
{ TPM_RIGHTBUTTON works better on Windows 3.x }
ButtonFlags: array[Boolean] of UINT = (TPM_RIGHTBUTTON, TPM_LEFTBUTTON);
AlignFlags: array[TPopupAlignment] of UINT = (TPM_LEFTALIGN, TPM_RIGHTALIGN,
TPM_CENTERALIGN);
{$ENDIF}
var
Popup, ShowMenu, RemoveClicks: Boolean;
SaveAlignment: TPopupAlignment;
{$IFDEF TB97D4}
SaveTrackButton: TTrackButton;
{$ENDIF}
PopupPt: TPoint;
RepostList: TList; {pointers to TMsg's}
Msg: TMsg;
Repost: Boolean;
I: Integer;
P: TPoint;
Form: {$IFDEF TB97D3} TCustomForm {$ELSE} TForm {$ENDIF};
DockPos: TGetToolbarDockPosType;
begin
if FRepeating and not FMenuIsDown then begin
inherited;
Exit;
end;
FInClick := True;
try
if (GroupIndex <> 0) and not FMenuIsDown then
SetDown (not FDown);
Popup := FUsesDropdown and (not FDropdownCombo or FMenuIsDown);
if ButtonsStayDown or Popup then begin
if FState in [bsUp, bsMouseIn] then begin
FState := bsDown;
Redraw (True);
end;
end
else begin
if FState = bsDown then begin
if FDown and (FGroupIndex <> 0) then
FState := bsExclusive
else
FState := bsUp;
Redraw (True);
end;
end;
{ Stop tracking }
MouseLeft;
if not Popup then begin
Form := GetParentForm(Self);
if Form <> nil then Form.ModalResult := ModalResult;
inherited;
end
else begin
if not FDropdownCombo then
inherited;
{ It must release its capture before displaying the popup menu since
this control uses csCaptureMouse. If it doesn't, the VCL seems to
get confused and think the mouse is still captured even after the
popup menu is displayed, causing mouse problems after the menu is
dismissed. }
MouseCapture := False;
ShowMenu := Assigned(FDropdownMenu);
RemoveClicks := True;
if Assigned(FOnDropdown) then
FOnDropdown (Self, ShowMenu, RemoveClicks);
try
if Assigned(FDropdownMenu) and ShowMenu then begin
SaveAlignment := DropdownMenu.Alignment;
{$IFDEF TB97D4}
SaveTrackButton := DropdownMenu.TrackButton;
{$ENDIF}
try
DropdownMenu.Alignment := paLeft;
PopupPt := Point(0, Height);
if Assigned(GetToolbarDockPosProc) then begin
DockPos := GetToolbarDockPosProc(Parent);
{ Drop out right or left side }
case DockPos of
gtpLeft: PopupPt := Point(Width, 0);
gtpRight: begin
PopupPt := Point(0, 0);
DropdownMenu.Alignment := paRight;
end;
end;
end;
PopupPt := ClientToScreen(PopupPt);
with DropdownMenu do begin
PopupComponent := Self;
{ In Delphi versions prior to 4 it avoids using the Popup method
of TPopupMenu because it always uses the "track right button"
flag (which disallowed the "click and drag" selecting motion many
people are accustomed to). Delphi 4 has a TrackButton property
to control the tracking button, so it can use the Popup method. }
{$IFNDEF TB97D4}
if (ClassType = TPopupMenu) and Assigned(DropdownList) then begin
if Assigned(OnPopup) then
OnPopup (DropdownMenu);
TrackPopupMenu (Handle, AlignFlags[Alignment] or ButtonFlags[NewStyleControls],
PopupPt.X, PopupPt.Y, 0, DropdownList.Window, nil)
end
else begin
{$ELSE}
if NewStyleControls then
TrackButton := tbLeftButton
else
TrackButton := tbRightButton;
{$ENDIF}
Popup (PopupPt.X, PopupPt.Y);
{$IFNDEF TB97D4}
end;
{$ENDIF}
end;
finally
DropdownMenu.Alignment := SaveAlignment;
{$IFDEF TB97D4}
DropdownMenu.TrackButton := SaveTrackButton;
{$ENDIF}
end;
end;
finally
if RemoveClicks then begin
{ To prevent a mouse click from redisplaying the menu, filter all
mouse up/down messages, and repost the ones that don't need
removing. This is sort of bulky, but it's the only way I could
find that works perfectly and like Office 97. }
RepostList := TList.Create;
try
while PeekMessage(Msg, 0, WM_LBUTTONDOWN, WM_MBUTTONDBLCLK,
PM_REMOVE or PM_NOYIELD) do
{ ^ The WM_LBUTTONDOWN to WM_MBUTTONDBLCLK range encompasses all
of the DOWN and DBLCLK messages for the three buttons }
with Msg do begin
Repost := True;
case Message of
WM_QUIT: begin
{ Throw back any WM_QUIT messages }
PostQuitMessage (wParam);
Break;
end;
WM_LBUTTONDOWN, WM_LBUTTONDBLCLK,
WM_RBUTTONDOWN, WM_RBUTTONDBLCLK,
WM_MBUTTONDOWN, WM_MBUTTONDBLCLK: begin
P := SmallPointToPoint(TSmallPoint(lParam));
Windows.ClientToScreen (hwnd, P);
if FindDragTarget(P, True) = Self then
Repost := False;
end;
end;
if Repost then begin
RepostList.Add (AllocMem(SizeOf(TMsg)));
PMsg(RepostList.Last)^ := Msg;
end;
end;
finally
for I := 0 to RepostList.Count-1 do begin
with PMsg(RepostList[I])^ do
PostMessage (hwnd, message, wParam, lParam);
FreeMem (RepostList[I]);
end;
RepostList.Free;
end;
end;
end;
end;
finally
FInClick := False;
if FState = bsDown then
FState := bsUp;
{ Need to check if it's destroying in case the OnClick handler freed
the button. If it doesn't check this here, it can sometimes cause an
access violation }
if not(csDestroying in ComponentState) then begin
Redraw (True);
MouseLeft;
end;
end;
end;
function TToolbarButton97.GetPalette: HPALETTE;
begin
Result := Glyph.Palette;
end;
function TToolbarButton97.GetGlyph: TBitmap;
begin
Result := TButtonGlyph(FGlyph).Glyph;
end;
procedure TToolbarButton97.SetGlyph (Value: TBitmap);
begin
TButtonGlyph(FGlyph).Glyph := Value;
Redraw (True);
end;
function TToolbarButton97.GetGlyphMask: TBitmap;
begin
Result := TButtonGlyph(FGlyph).GlyphMask;
end;
procedure TToolbarButton97.SetGlyphMask (Value: TBitmap);
begin
TButtonGlyph(FGlyph).GlyphMask := Value;
Redraw (True);
end;
procedure TToolbarButton97.SetHighlightWhenDown (Value: Boolean);
begin
if FHighlightWhenDown <> Value then begin
FHighlightWhenDown := Value;
if Down then
Redraw (True);
end;
end;
function TToolbarButton97.GetImageIndex: Integer;
begin
Result := TButtonGlyph(FGlyph).FImageIndex;
end;
procedure TToolbarButton97.SetImageIndex (Value: Integer);
begin
if TButtonGlyph(FGlyph).FImageIndex <> Value then begin
TButtonGlyph(FGlyph).FImageIndex := Value;
if Assigned(TButtonGlyph(FGlyph).FImageList) then
TButtonGlyph(FGlyph).GlyphChanged (nil);
end;
end;
function TToolbarButton97.GetImages: TCustomImageList;
begin
Result := TButtonGlyph(FGlyph).FImageList;
end;
procedure TToolbarButton97.SetImages (Value: TCustomImageList);
begin
with TButtonGlyph(FGlyph) do
if FImageList <> Value then begin
if FImageList <> nil then
FImageList.UnRegisterChanges (FImageChangeLink);
FImageList := Value;
if FImageList <> nil then begin
if FImageChangeLink = nil then begin
FImageChangeLink := TChangeLink.Create;
FImageChangeLink.OnChange := GlyphChanged;
end;
FImageList.RegisterChanges (FImageChangeLink);
FImageList.FreeNotification (Self);
end
else begin
FImageChangeLink.Free;
FImageChangeLink := nil;
end;
UpdateNumGlyphs;
end;
end;
function TToolbarButton97.GetNumGlyphs: TNumGlyphs97;
begin
Result := TButtonGlyph(FGlyph).NumGlyphs;
end;
procedure TToolbarButton97.SetNumGlyphs (Value: TNumGlyphs97);
begin
if Value < Low(TNumGlyphs97) then
Value := Low(TNumGlyphs97)
else
if Value > High(TNumGlyphs97) then
Value := High(TNumGlyphs97);
if Value <> TButtonGlyph(FGlyph).NumGlyphs then begin
TButtonGlyph(FGlyph).NumGlyphs := Value;
Redraw (True);
end;
end;
procedure TToolbarButton97.GlyphChanged(Sender: TObject);
begin
Redraw (True);
end;
procedure TToolbarButton97.UpdateExclusive;
var
I: Integer;
Ctl: TControl;
begin
if (FGroupIndex <> 0) and (Parent <> nil) then
with Parent do
for I := 0 to ControlCount-1 do begin
Ctl := Controls[I];
if (Ctl <> Self) and (Ctl is TToolbarButton97) then
with TToolbarButton97(Ctl) do
if FGroupIndex = Self.FGroupIndex then begin
if Self.Down and FDown then begin
FDown := False;
FState := bsUp;
Redraw (True);
end;
FAllowAllUp := Self.AllowAllUp;
end;
end;
end;
procedure TToolbarButton97.SetDown (Value: Boolean);
begin
if FGroupIndex = 0 then
Value := False;
if Value <> FDown then begin
if FDown and (not FAllowAllUp) then Exit;
FDown := Value;
if not Enabled then
FState := bsDisabled
else begin
if Value then
FState := bsExclusive
else
FState := bsUp;
end;
Redraw (True);
if Value then UpdateExclusive;
end;
end;
procedure TToolbarButton97.SetFlat (Value: Boolean);
begin
if FFlat <> Value then begin
FFlat := Value;
if FOpaque or not FFlat then
ControlStyle := ControlStyle + [csOpaque]
else
ControlStyle := ControlStyle - [csOpaque];
Redraw (True);
end;
end;
procedure TToolbarButton97.SetGroupIndex (Value: Integer);
begin
if FGroupIndex <> Value then begin
FGroupIndex := Value;
UpdateExclusive;
end;
end;
procedure TToolbarButton97.SetLayout (Value: TButtonLayout);
begin
if FLayout <> Value then begin
FLayout := Value;
Redraw (True);
end;
end;
procedure TToolbarButton97.SetMargin (Value: Integer);
begin
if (FMargin <> Value) and (Value >= -1) then begin
FMargin := Value;
Redraw (True);
end;
end;
procedure TToolbarButton97.SetNoBorder (Value: Boolean);
begin
if FNoBorder <> Value then begin
FNoBorder := Value;
Invalidate;
end;
end;
procedure TToolbarButton97.SetOldDisabledStyle (Value: Boolean);
begin
if FOldDisabledStyle <> Value then begin
FOldDisabledStyle := Value;
with TButtonGlyph(FGlyph) do begin
FOldDisabledStyle := Value;
Invalidate;
end;
Redraw (True);
end;
end;
procedure TToolbarButton97.SetOpaque (Value: Boolean);
begin
if FOpaque <> Value then begin
FOpaque := Value;
if FOpaque or not FFlat then
ControlStyle := ControlStyle + [csOpaque]
else
ControlStyle := ControlStyle - [csOpaque];
Invalidate;
end;
end;
procedure TToolbarButton97.Redraw (const Erase: Boolean);
var
AddedOpaque: Boolean;
begin
if FOpaque or not FFlat or not Erase then begin
{ Temporarily add csOpaque to the style. This prevents Invalidate from
erasing, which isn't needed when Erase is false. }
AddedOpaque := False;
if not(csOpaque in ControlStyle) then begin
AddedOpaque := True;
ControlStyle := ControlStyle + [csOpaque];
end;
try
Invalidate;
finally
if AddedOpaque then
ControlStyle := ControlStyle - [csOpaque];
end;
end
else
if not(FOpaque or not FFlat) then
Invalidate;
end;
procedure TToolbarButton97.SetSpacing (Value: Integer);
begin
if Value <> FSpacing then begin
FSpacing := Value;
Redraw (True);
end;
end;
procedure TToolbarButton97.SetAllowAllUp (Value: Boolean);
begin
if FAllowAllUp <> Value then begin
FAllowAllUp := Value;
UpdateExclusive;
end;
end;
procedure TToolbarButton97.SetDropdownMenu (Value: TPopupMenu);
var
NewUsesDropdown: Boolean;
begin
if FDropdownMenu <> Value then begin
FDropdownMenu := Value;
if Assigned(Value) then begin
Value.FreeNotification (Self);
{$IFNDEF TB97D4}
if DropdownList = nil then
DropdownList := TDropdownList.Create(nil);
DropdownList.AddMenu (Value);
{$ENDIF}
end;
NewUsesDropdown := FDropdownAlways or Assigned(Value);
if FUsesDropdown <> NewUsesDropdown then begin
FUsesDropdown := NewUsesDropdown;
if FDropdownArrow or FDropdownCombo then
Redraw (True);
end;
end;
end;
procedure TToolbarButton97.SetWordWrap (Value: Boolean);
begin
if FWordWrap <> Value then begin
FWordWrap := Value;
Redraw (True);
end;
end;
procedure TToolbarButton97.SetAlignment (Value: TAlignment);
begin
if FAlignment <> Value then begin
FAlignment := Value;
Redraw (True);
end;
end;
procedure TToolbarButton97.SetDropdownAlways (Value: Boolean);
var
NewUsesDropdown: Boolean;
begin
if FDropdownAlways <> Value then begin
FDropdownAlways := Value;
NewUsesDropdown := Value or Assigned(FDropdownMenu);
if FUsesDropdown <> NewUsesDropdown then begin
FUsesDropdown := NewUsesDropdown;
if FDropdownArrow or FDropdownCombo then
Redraw (True);
end;
end;
end;
procedure TToolbarButton97.SetDropdownArrow (Value: Boolean);
begin
if FDropdownArrow <> Value then begin
FDropdownArrow := Value;
Redraw (True);
end;
end;
procedure TToolbarButton97.SetDropdownArrowWidth (Value: Integer);
var
Diff: Integer;
begin
if Value < 7 then Value := 7;
if FDropdownArrowWidth <> Value then begin
Diff := Value - FDropdownArrowWidth;
FDropdownArrowWidth := Value;
if not(csLoading in ComponentState) and FDropdownCombo then
Width := Width + Diff;
Redraw (True);
end;
end;
procedure TToolbarButton97.SetDropdownCombo (Value: Boolean);
var
W: Integer;
begin
if FDropdownCombo <> Value then begin
FDropdownCombo := Value;
if not(csLoading in ComponentState) then begin
if Value then
Width := Width + (DropdownArrowWidth + DropdownComboSpace)
else begin
W := Width - (DropdownArrowWidth + DropdownComboSpace);
if W < 1 then W := 1;
Width := W;
end;
end;
Redraw (True);
end;
end;
procedure TToolbarButton97.SetDisplayMode (Value: TButtonDisplayMode);
begin
if FDisplayMode <> Value then begin
FDisplayMode := Value;
Redraw (True);
end;
end;
function TToolbarButton97.GetCallDormant: Boolean;
begin
Result := TButtonGlyph(FGlyph).FCallDormant;
end;
procedure TToolbarButton97.SetCallDormant (Value: Boolean);
begin
TButtonGlyph(FGlyph).FCallDormant := Value;
end;
function TToolbarButton97.GetVersion: TToolbar97Version;
begin
Result := Toolbar97VersionPropText;
end;
procedure TToolbarButton97.SetVersion (const Value: TToolbar97Version);
begin
{ write method required for the property to show up in Object Inspector }
end;
{$IFDEF TB97D4}
function TToolbarButton97.IsCheckedStored: Boolean;
begin
Result := (ActionLink = nil) or not TToolbarButton97ActionLink(ActionLink).IsCheckedLinked;
end;
function TToolbarButton97.IsHelpContextStored: Boolean;
begin
Result := (ActionLink = nil) or not TToolbarButton97ActionLink(ActionLink).IsHelpContextLinked;
end;
function TToolbarButton97.IsImageIndexStored: Boolean;
begin
Result := (ActionLink = nil) or not TToolbarButton97ActionLink(ActionLink).IsImageIndexLinked;
end;
procedure TToolbarButton97.ActionChange (Sender: TObject; CheckDefaults: Boolean);
begin
inherited;
if Sender is TCustomAction then
with TCustomAction(Sender) do
begin
if not CheckDefaults or (Self.Down = False) then
Self.Down := Checked;
if not CheckDefaults or (Self.HelpContext = 0) then
Self.HelpContext := HelpContext;
if not CheckDefaults or (Self.ImageIndex = -1) then
Self.ImageIndex := ImageIndex;
end;
end;
function TToolbarButton97.GetActionLinkClass: TControlActionLinkClass;
begin
Result := TToolbarButton97ActionLink;
end;
procedure TToolbarButton97.AssignTo (Dest: TPersistent);
begin
inherited;
if Dest is TCustomAction then
TCustomAction(Dest).Checked := Self.Down;
end;
{$ENDIF}
procedure TToolbarButton97.WMLButtonDblClk (var Message: TWMLButtonDblClk);
begin
inherited;
if FDown then DblClick;
end;
procedure TToolbarButton97.CMEnabledChanged (var Message: TMessage);
begin
if not Enabled then begin
FState := bsDisabled;
FMouseInControl := False;
FMouseIsDown := False;
RemoveButtonMouseTimer;
Perform (WM_CANCELMODE, 0, 0);
end
else
if FState = bsDisabled then
if FDown and (FGroupIndex <> 0) then
FState := bsExclusive
else
FState := bsUp;
Redraw (True);
end;
procedure TToolbarButton97.CMDialogChar (var Message: TCMDialogChar);
begin
with Message do
if IsAccel(CharCode, Caption) and Assigned(Parent) and Parent.CanFocus and
Enabled and Visible and (DisplayMode <> dmGlyphOnly) then begin
{ NOTE: There is a bug in TSpeedButton where accelerator keys are still
processed even when the button is not visible. The 'and Visible'
corrects it, so TToolbarButton97 doesn't have this problem. }
Click;
Result := 1;
end
else
inherited;
end;
procedure TToolbarButton97.CMDialogKey (var Message: TCMDialogKey);
begin
with Message do
if (((CharCode = VK_RETURN) and FDefault) or
((CharCode = VK_ESCAPE) and FCancel)) and
(KeyDataToShiftState(Message.KeyData) = []) and
Assigned(Parent) and Parent.CanFocus and Enabled and Visible then begin
Click;
Result := 1;
end
else
inherited;
end;
procedure TToolbarButton97.CMFontChanged (var Message: TMessage);
begin
Redraw (True);
end;
procedure TToolbarButton97.CMTextChanged (var Message: TMessage);
begin
Redraw (True);
end;
procedure TToolbarButton97.CMSysColorChange (var Message: TMessage);
begin
inherited;
if Assigned(Pattern) and
((PatternBtnFace <> TColor(GetSysColor(COLOR_BTNFACE))) or
(PatternBtnHighlight <> TColor(GetSysColor(COLOR_BTNHIGHLIGHT)))) then begin
Pattern.Free;
Pattern := nil;
end;
with TButtonGlyph(FGlyph) do begin
Invalidate;
CreateButtonGlyph (FState);
end;
end;
procedure TToolbarButton97.MouseEntered;
begin
if Enabled and not FMouseInControl then begin
FMouseInControl := True;
if FState = bsUp then
FState := bsMouseIn;
if FFlat or (NumGlyphs >= 5) then
Redraw (FDown or (NumGlyphs >= 5));
if Assigned(FOnMouseEnter) then
FOnMouseEnter (Self);
end;
end;
procedure TToolbarButton97.MouseLeft;
var
OldState: TButtonState97;
begin
if Enabled and FMouseInControl and not FMouseIsDown then begin
FMouseInControl := False;
RemoveButtonMouseTimer;
OldState := FState;
if (FState = bsMouseIn) or (not FInClick and (FState = bsDown)) then begin
if FDown and (FGroupIndex <> 0) then
FState := bsExclusive
else
FState := bsUp;
end;
if FFlat or ((NumGlyphs >= 5) or ((OldState = bsMouseIn) xor (FState <> OldState))) then
Redraw (True);
if Assigned(FOnMouseExit) then
FOnMouseExit (Self);
end;
end;
procedure TToolbarButton97.ButtonMouseTimerHandler (Sender: TObject);
var
P: TPoint;
begin
{ The button mouse timer is used to periodically check if mouse has left.
Normally it receives a CM_MOUSELEAVE, but the VCL does not send a
CM_MOUSELEAVE if the mouse is moved quickly from the button to another
application's window. For some reason, this problem doesn't seem to occur
on Windows NT 4 -- only 95 and 3.x.
The timer (which ticks 8 times a second) is only enabled when the
application is active and the mouse is over a button, so it uses virtually
no processing power.
For something interesting to try: If you want to know just how often this
is called, try putting a Beep call in here }
GetCursorPos (P);
if FindDragTarget(P, True) <> Self then
MouseLeft;
end;
{ TEdit97 - internal }
constructor TEdit97.Create (AOwner: TComponent);
begin
inherited;
AutoSize := False;
Ctl3D := False;
BorderStyle := bsNone;
ControlStyle := ControlStyle - [csFramed]; {fixes a VCL bug with Win 3.x}
Height := 19;
if Edit97Count = 0 then
Register97ControlClass (TEdit97);
Inc (Edit97Count);
end;
destructor TEdit97.Destroy;
begin
Dec (Edit97Count);
if Edit97Count = 0 then
Unregister97ControlClass (TEdit97);
inherited;
end;
procedure TEdit97.CMMouseEnter (var Message: TMessage);
begin
inherited;
MouseInControl := True;
DrawNCArea (False, 0, 0);
end;
procedure TEdit97.CMMouseLeave (var Message: TMessage);
begin
inherited;
MouseInControl := False;
DrawNCArea (False, 0, 0);
end;
procedure TEdit97.NewAdjustHeight;
var
DC: HDC;
SaveFont: HFONT;
Metrics: TTextMetric;
begin
DC := GetDC(0);
SaveFont := SelectObject(DC, Font.Handle);
GetTextMetrics (DC, Metrics);
SelectObject (DC, SaveFont);
ReleaseDC (0, DC);
Height := Metrics.tmHeight + 6;
end;
procedure TEdit97.Loaded;
begin
inherited;
if not(csDesigning in ComponentState) then
NewAdjustHeight;
end;
procedure TEdit97.CMEnabledChanged (var Message: TMessage);
const
EnableColors: array[Boolean] of TColor = (clBtnFace, clWindow);
begin
inherited;
Color := EnableColors[Enabled];
{ Ensure non-client area is invalidated as well }
if HandleAllocated then
RedrawWindow (Handle, nil, 0, RDW_FRAME or RDW_INVALIDATE or RDW_ERASE or
RDW_NOCHILDREN);
end;
procedure TEdit97.CMFontChanged (var Message: TMessage);
begin
inherited;
if not((csDesigning in ComponentState) and (csLoading in ComponentState)) then
NewAdjustHeight;
end;
procedure TEdit97.WMSetFocus (var Message: TWMSetFocus);
begin
inherited;
if not(csDesigning in ComponentState) then
DrawNCArea (False, 0, 0);
end;
procedure TEdit97.WMKillFocus (var Message: TWMKillFocus);
begin
inherited;
if not(csDesigning in ComponentState) then
DrawNCArea (False, 0, 0);
end;
procedure TEdit97.WMNCCalcSize (var Message: TWMNCCalcSize);
begin
InflateRect (Message.CalcSize_Params^.rgrc[0], -3, -3);
end;
procedure TEdit97.WMNCPaint (var Message: TMessage);
begin
DrawNCArea (False, 0, HRGN(Message.WParam));
end;
procedure TEdit97.DrawNCArea (const DrawToDC: Boolean; const ADC: HDC;
const Clip: HRGN);
var
DC: HDC;
R: TRect;
BtnFaceBrush, WindowBrush: HBRUSH;
begin
if not DrawToDC then
DC := GetWindowDC(Handle)
else
DC := ADC;
try
{ Use update region }
if not DrawToDC then
SelectNCUpdateRgn (Handle, DC, Clip);
{ This works around WM_NCPAINT problem described at top of source code }
{no! R := Rect(0, 0, Width, Height);}
GetWindowRect (Handle, R); OffsetRect (R, -R.Left, -R.Top);
BtnFaceBrush := GetSysColorBrush(COLOR_BTNFACE);
WindowBrush := GetSysColorBrush(COLOR_WINDOW);
if ((csDesigning in ComponentState) and Enabled) or
(not(csDesigning in ComponentState) and
(Focused or (MouseInControl and not ControlIs97Control(Screen.ActiveControl)))) then begin
DrawEdge (DC, R, BDR_SUNKENOUTER, BF_RECT or BF_ADJUST);
with R do begin
FillRect (DC, Rect(Left, Top, Left+1, Bottom-1), BtnFaceBrush);
FillRect (DC, Rect(Left, Top, Right-1, Top+1), BtnFaceBrush);
end;
DrawEdge (DC, R, BDR_SUNKENINNER, BF_BOTTOMRIGHT);
InflateRect (R, -1, -1);
FrameRect (DC, R, WindowBrush);
end
else begin
FrameRect (DC, R, BtnFaceBrush);
InflateRect (R, -1, -1);
FrameRect (DC, R, BtnFaceBrush);
InflateRect (R, -1, -1);
FrameRect (DC, R, WindowBrush);
end;
finally
if not DrawToDC then
ReleaseDC (Handle, DC);
end;
end;
procedure EditNCPaintProc (Wnd: HWND; DC: HDC; AppData: Longint);
begin
TEdit97(AppData).DrawNCArea (True, DC, 0);
end;
procedure TEdit97.WMPrint (var Message: TMessage);
begin
HandleWMPrint (Handle, Message, EditNCPaintProc, Longint(Self));
end;
procedure TEdit97.WMPrintClient (var Message: TMessage);
begin
HandleWMPrintClient (Self, Message);
end;
function TEdit97.GetVersion: TToolbar97Version;
begin
Result := Toolbar97VersionPropText;
end;
procedure TEdit97.SetVersion (const Value: TToolbar97Version);
begin
{ write method required for the property to show up in Object Inspector }
end;
{$IFNDEF TB97D4}
initialization
finalization
DropdownList.Free;
{$ENDIF}
end.