home *** CD-ROM | disk | FTP | other *** search
/ Chip 1999 January / Chip_1999-01_cd.bin / zkuste / delphi / D4 / COOLFORM.ZIP / CoolButton.pas < prev    next >
Encoding:
Pascal/Delphi Source File  |  1998-06-04  |  30.6 KB  |  1,134 lines

  1. unit CoolButton;
  2.  
  3. {$S-,W-,R-}
  4. {$C PRELOAD}
  5.  
  6. interface
  7.  
  8. uses Windows, Messages, Classes, Controls, Forms, Graphics, StdCtrls,
  9.     ExtCtrls, CommCtrl;
  10.  
  11. type
  12.     TButtonLayout = (blGlyphLeft, blGlyphRight, blGlyphTop, blGlyphBottom);
  13.     TButtonState = (bsUp, bsDisabled, bsDown, bsExclusive);
  14.     TButtonStyle = (bsAutoDetect, bsWin31, bsNew);
  15.     TNumGlyphs = 1..4;
  16.  
  17.     TCoolButton = class(TGraphicControl)
  18.     private
  19.         FGroupIndex: Integer;
  20.         FGlyph: Pointer;
  21.         FDown: Boolean;
  22.         FDragging: Boolean;
  23.         FAllowAllUp: Boolean;
  24.         FLayout: TButtonLayout;
  25.         FSpacing: Integer;
  26.         FMargin: Integer;
  27.         FMouseInControl: Boolean;
  28. //        FMouseinMask: Boolean;
  29.         procedure GlyphChanged(Sender: TObject);
  30.         procedure UpdateExclusive;
  31.         function GetGlyph: TBitmap;
  32.         procedure SetGlyph(Value: TBitmap);
  33.         function GetNumGlyphs: TNumGlyphs;
  34.     procedure SetNumGlyphs(Value: TNumGlyphs);
  35.     procedure SetDown(Value: Boolean);
  36.     procedure SetAllowAllUp(Value: Boolean);
  37.     procedure SetGroupIndex(Value: Integer);
  38.     procedure SetLayout(Value: TButtonLayout);
  39.     procedure SetSpacing(Value: Integer);
  40.     procedure SetMargin(Value: Integer);
  41.     procedure UpdateTracking;
  42.     procedure WMLButtonDblClk(var Message: TWMLButtonDown); message WM_LBUTTONDBLCLK;
  43.     procedure CMEnabledChanged(var Message: TMessage); message CM_ENABLEDCHANGED;
  44.     procedure CMButtonPressed(var Message: TMessage); message CM_BUTTONPRESSED;
  45.     procedure CMDialogChar(var Message: TCMDialogChar); message CM_DIALOGCHAR;
  46.         procedure CMFontChanged(var Message: TMessage); message CM_FONTCHANGED;
  47.     procedure CMTextChanged(var Message: TMessage); message CM_TEXTCHANGED;
  48.     procedure CMSysColorChange(var Message: TMessage); message CM_SYSCOLORCHANGE;
  49.         procedure CMMouseLeave(var Message: TMessage); message CM_MOUSELEAVE;
  50.         procedure WMEraseBkgnd( var message:TWMEraseBkgnd); message WM_ERASEBKGND;
  51.         procedure WMPaint( var message:TWMPaint); message WM_PAINT;
  52.         procedure WMNCPaint( var message:TWMNCPaint); message WM_NCPAINT;
  53.   protected
  54.     FState: TButtonState;
  55.     function GetPalette: HPALETTE; override;
  56.     procedure Loaded; override;
  57.     procedure MouseDown(Button: TMouseButton; Shift: TShiftState;
  58.       X, Y: Integer); override;
  59.     procedure MouseMove(Shift: TShiftState; X, Y: Integer); override;
  60.     procedure MouseUp(Button: TMouseButton; Shift: TShiftState;
  61.       X, Y: Integer); override;
  62.     procedure Paint; override;
  63.   public
  64.     constructor Create(AOwner: TComponent); override;
  65.     destructor Destroy; override;
  66.     procedure Click; override;
  67.   published
  68.     property AllowAllUp: Boolean read FAllowAllUp write SetAllowAllUp default False;
  69.     property GroupIndex: Integer read FGroupIndex write SetGroupIndex default 0;
  70.     property Down: Boolean read FDown write SetDown default False;
  71.     property Caption;
  72.     property Enabled;
  73. property Font;
  74.     property Glyph: TBitmap read GetGlyph write SetGlyph;
  75.     property Layout: TButtonLayout read FLayout write SetLayout default blGlyphLeft;
  76.     property Margin: Integer read FMargin write SetMargin default -1;
  77.     property NumGlyphs: TNumGlyphs read GetNumGlyphs write SetNumGlyphs default 4;
  78.     property ParentFont;
  79.     property ParentShowHint;
  80.     property ShowHint;
  81.     property Spacing: Integer read FSpacing write SetSpacing default 4;
  82.     property Visible;
  83.     property OnClick;
  84.     property OnDblClick;
  85.     property OnMouseDown;
  86.     property OnMouseMove;
  87.     property OnMouseUp;
  88.   end;
  89.  
  90. function DrawButtonFace(Canvas: TCanvas; const Client: TRect;
  91.   BevelWidth: Integer; Style: TButtonStyle; IsRounded, IsDown,
  92.   IsFocused: Boolean): TRect;
  93.  
  94. procedure Register;
  95.  
  96. implementation
  97.  
  98. uses Consts, SysUtils;
  99.  
  100.  
  101. procedure Register;
  102. begin
  103.     RegisterComponents('Cool!', [TCoolButton]);
  104. end;
  105.  
  106.  
  107.  
  108. { DrawButtonFace - returns the remaining usable area inside the Client rect.}
  109. function DrawButtonFace(Canvas: TCanvas; const Client: TRect;
  110.   BevelWidth: Integer; Style: TButtonStyle; IsRounded, IsDown,
  111.   IsFocused: Boolean): TRect;
  112. var
  113.   NewStyle: Boolean;
  114.   R: TRect;
  115.   DC: THandle;
  116. begin
  117.   NewStyle := ((Style = bsAutoDetect) and NewStyleControls) or (Style = bsNew);
  118.  
  119.   R := Client;
  120.   with Canvas do
  121.   begin
  122.     if NewStyle then
  123.     begin
  124.       Brush.Color := clBtnFace;
  125.       Brush.Style := bsSolid;
  126.       DC := Canvas.Handle;    { Reduce calls to GetHandle }
  127.  
  128.       if IsDown then
  129.       begin    { DrawEdge is faster than Polyline }
  130.         DrawEdge(DC, R, BDR_SUNKENINNER, BF_TOPLEFT);              { black     }
  131.         DrawEdge(DC, R, BDR_SUNKENOUTER, BF_BOTTOMRIGHT);          { btnhilite }
  132.         Dec(R.Bottom);
  133.         Dec(R.Right);
  134.         Inc(R.Top);
  135.         Inc(R.Left);
  136.         DrawEdge(DC, R, BDR_SUNKENOUTER, BF_TOPLEFT or BF_MIDDLE); { btnshadow }
  137.       end
  138.       else
  139.       begin
  140.         DrawEdge(DC, R, BDR_RAISEDOUTER, BF_BOTTOMRIGHT);          { black }
  141.         Dec(R.Bottom);
  142.         Dec(R.Right);
  143.         DrawEdge(DC, R, BDR_RAISEDINNER, BF_TOPLEFT);              { btnhilite }
  144.         Inc(R.Top);
  145.         Inc(R.Left);
  146.         DrawEdge(DC, R, BDR_RAISEDINNER, BF_BOTTOMRIGHT or BF_MIDDLE); { btnshadow }
  147.       end;
  148.     end
  149.     else
  150.     begin
  151.       Pen.Color := clWindowFrame;
  152.       Brush.Color := clBtnFace;
  153.       Brush.Style := bsSolid;
  154.       Rectangle(R.Left, R.Top, R.Right, R.Bottom);
  155.  
  156.       { round the corners - only applies to Win 3.1 style buttons }
  157.       if IsRounded then
  158.       begin
  159.         Pixels[R.Left, R.Top] := clBtnFace;
  160.         Pixels[R.Left, R.Bottom - 1] := clBtnFace;
  161.         Pixels[R.Right - 1, R.Top] := clBtnFace;
  162.         Pixels[R.Right - 1, R.Bottom - 1] := clBtnFace;
  163.       end;
  164.  
  165.       if IsFocused then
  166.       begin
  167.         InflateRect(R, -1, -1);
  168.         Brush.Style := bsClear;
  169.         Rectangle(R.Left, R.Top, R.Right, R.Bottom);
  170.       end;
  171.  
  172.       InflateRect(R, -1, -1);
  173.       if not IsDown then
  174.         Frame3D(Canvas, R, clBtnHighlight, clBtnShadow, BevelWidth)
  175.       else
  176.       begin
  177.         Pen.Color := clBtnShadow;
  178.         PolyLine([Point(R.Left, R.Bottom - 1), Point(R.Left, R.Top),
  179.           Point(R.Right, R.Top)]);
  180.       end;
  181.     end;
  182.   end;
  183.  
  184.   Result := Rect(Client.Left + 1, Client.Top + 1,
  185.     Client.Right - 2, Client.Bottom - 2);
  186.   if IsDown then OffsetRect(Result, 1, 1);
  187. end;
  188.  
  189.  
  190. type
  191.   TGlyphList = class(TImageList)
  192.   private
  193.     Used: TBits;
  194.     FCount: Integer;
  195.     function AllocateIndex: Integer;
  196.   public
  197.     constructor CreateSize(AWidth, AHeight: Integer);
  198.     destructor Destroy; override;
  199.     function AddMasked(Image: TBitmap; MaskColor: TColor): Integer;
  200.     procedure Delete(Index: Integer);
  201.     property Count: Integer read FCount;
  202.   end;
  203.  
  204.   TGlyphCache = class
  205.   private
  206.     GlyphLists: TList;
  207.   public
  208.     constructor Create;
  209.     destructor Destroy; override;
  210.     function GetList(AWidth, AHeight: Integer): TGlyphList;
  211.     procedure ReturnList(List: TGlyphList);
  212.     function Empty: Boolean;
  213.   end;
  214.  
  215.   TButtonGlyph = class
  216.   private
  217.     FOriginal: TBitmap;
  218.     FGlyphList: TGlyphList;
  219.     FIndexs: array[TButtonState] of Integer;
  220.     FTransparentColor: TColor;
  221.     FNumGlyphs: TNumGlyphs;
  222.     FOnChange: TNotifyEvent;
  223.     procedure GlyphChanged(Sender: TObject);
  224.     procedure SetGlyph(Value: TBitmap);
  225.     procedure SetNumGlyphs(Value: TNumGlyphs);
  226.     procedure Invalidate;
  227.     function CreateButtonGlyph(State: TButtonState): Integer;
  228.     procedure DrawButtonGlyph(Canvas: TCanvas; const GlyphPos: TPoint;
  229.       State: TButtonState; Transparent: Boolean);
  230.     procedure DrawButtonText(Canvas: TCanvas; const Caption: string;
  231.       TextBounds: TRect; State: TButtonState);
  232.     procedure CalcButtonLayout(Canvas: TCanvas; const Client: TRect;
  233.       const Offset: TPoint; const Caption: string; Layout: TButtonLayout;
  234.       Margin, Spacing: Integer; var GlyphPos: TPoint; var TextBounds: TRect);
  235.   public
  236.     constructor Create;
  237.     destructor Destroy; override;
  238.     { return the text rectangle }
  239.     function Draw(Canvas: TCanvas; const Client: TRect; const Offset: TPoint;
  240.       const Caption: string; Layout: TButtonLayout; Margin, Spacing: Integer;
  241.       State: TButtonState; Transparent: Boolean): TRect;
  242.     property Glyph: TBitmap read FOriginal write SetGlyph;
  243.     property NumGlyphs: TNumGlyphs read FNumGlyphs write SetNumGlyphs;
  244.     property OnChange: TNotifyEvent read FOnChange write FOnChange;
  245.   end;
  246.  
  247. { TGlyphList }
  248.  
  249. constructor TGlyphList.CreateSize(AWidth, AHeight: Integer);
  250. begin
  251.   inherited CreateSize(AWidth, AHeight);
  252.   Used := TBits.Create;
  253. end;
  254.  
  255. destructor TGlyphList.Destroy;
  256. begin
  257.   Used.Free;
  258.   inherited Destroy;
  259. end;
  260.  
  261. function TGlyphList.AllocateIndex: Integer;
  262. begin
  263.   Result := Used.OpenBit;
  264.   if Result >= Used.Size then
  265.   begin
  266.     Result := inherited Add(nil, nil);
  267.     Used.Size := Result + 1;
  268.   end;
  269.   Used[Result] := True;
  270. end;
  271.  
  272. function TGlyphList.AddMasked(Image: TBitmap; MaskColor: TColor): Integer;
  273. begin
  274.   Result := AllocateIndex;
  275.   ReplaceMasked(Result, Image, MaskColor);
  276.   Inc(FCount);
  277. end;
  278.  
  279. procedure TGlyphList.Delete(Index: Integer);
  280. begin
  281.   if Used[Index] then
  282.   begin
  283.     Dec(FCount);
  284.     Used[Index] := False;
  285.   end;
  286. end;
  287.  
  288. { TGlyphCache }
  289.  
  290. constructor TGlyphCache.Create;
  291. begin
  292.   inherited Create;
  293.   GlyphLists := TList.Create;
  294. end;
  295.  
  296. destructor TGlyphCache.Destroy;
  297. begin
  298.   GlyphLists.Free;
  299.   inherited Destroy;
  300. end;
  301.  
  302. function TGlyphCache.GetList(AWidth, AHeight: Integer): TGlyphList;
  303. var
  304.   I: Integer;
  305. begin
  306.   for I := GlyphLists.Count - 1 downto 0 do
  307.   begin
  308.     Result := GlyphLists[I];
  309.     with Result do
  310.       if (AWidth = Width) and (AHeight = Height) then Exit;
  311.   end;
  312.   Result := TGlyphList.CreateSize(AWidth, AHeight);
  313.   GlyphLists.Add(Result);
  314. end;
  315.  
  316. procedure TGlyphCache.ReturnList(List: TGlyphList);
  317. begin
  318.   if List = nil then Exit;
  319.   if List.Count = 0 then
  320.   begin
  321.     GlyphLists.Remove(List);
  322.     List.Free;
  323.   end;
  324. end;
  325.  
  326. function TGlyphCache.Empty: Boolean;
  327. begin
  328.   Result := GlyphLists.Count = 0;
  329. end;
  330.  
  331. var
  332.   GlyphCache: TGlyphCache = nil;
  333.   Pattern: TBitmap = nil;
  334.   ButtonCount: Integer = 0;
  335.  
  336. procedure CreateBrushPattern;
  337. var
  338.   X, Y: Integer;
  339. begin
  340.   Pattern := TBitmap.Create;
  341.   Pattern.Width := 8;
  342.   Pattern.Height := 8;
  343.   with Pattern.Canvas do
  344.   begin
  345.     Brush.Style := bsSolid;
  346.     Brush.Color := clBtnFace;
  347.     FillRect(Rect(0, 0, Pattern.Width, Pattern.Height));
  348.     for Y := 0 to 7 do
  349.       for X := 0 to 7 do
  350.         if (Y mod 2) = (X mod 2) then  { toggles between even/odd pixles }
  351.           Pixels[X, Y] := clBtnHighlight;     { on even/odd rows }
  352.   end;
  353. end;
  354.  
  355.  
  356. { TButtonGlyph }
  357.  
  358. constructor TButtonGlyph.Create;
  359. var
  360.   I: TButtonState;
  361. begin
  362.   inherited Create;
  363.   FOriginal := TBitmap.Create;
  364.   FOriginal.OnChange := GlyphChanged;
  365.   FTransparentColor := clOlive;
  366.   FNumGlyphs := 1;
  367.   for I := Low(I) to High(I) do
  368.     FIndexs[I] := -1;
  369.   if GlyphCache = nil then GlyphCache := TGlyphCache.Create;
  370. end;
  371.  
  372. destructor TButtonGlyph.Destroy;
  373. begin
  374.   FOriginal.Free;
  375.   Invalidate;
  376.   if Assigned(GlyphCache) and GlyphCache.Empty then
  377.   begin
  378.     GlyphCache.Free;
  379.     GlyphCache := nil;
  380.   end;
  381.   inherited Destroy;
  382. end;
  383.  
  384. procedure TButtonGlyph.Invalidate;
  385. var
  386.   I: TButtonState;
  387. begin
  388.   for I := Low(I) to High(I) do
  389.   begin
  390.     if FIndexs[I] <> -1 then FGlyphList.Delete(FIndexs[I]);
  391.     FIndexs[I] := -1;
  392.   end;
  393.   GlyphCache.ReturnList(FGlyphList);
  394.   FGlyphList := nil;
  395. end;
  396.  
  397. procedure TButtonGlyph.GlyphChanged(Sender: TObject);
  398. begin
  399.   if Sender = FOriginal then
  400.   begin
  401.     FTransparentColor := FOriginal.TransparentColor;
  402.     Invalidate;
  403.     if Assigned(FOnChange) then FOnChange(Self);
  404.   end;
  405. end;
  406.  
  407. procedure TButtonGlyph.SetGlyph(Value: TBitmap);
  408. var
  409.   Glyphs: Integer;
  410. begin
  411.   Invalidate;
  412.   FOriginal.Assign(Value);
  413.   if (Value <> nil) and (Value.Height > 0) then
  414.   begin
  415.     FTransparentColor := Value.TransparentColor;
  416.     if Value.Width mod Value.Height = 0 then
  417.     begin
  418.       Glyphs := Value.Width div Value.Height;
  419.       if Glyphs > 4 then Glyphs := 1;
  420.       SetNumGlyphs(Glyphs);
  421.     end;
  422.   end;
  423. end;
  424.  
  425. procedure TButtonGlyph.SetNumGlyphs(Value: TNumGlyphs);
  426. begin
  427.   if (Value <> FNumGlyphs) and (Value > 0) then
  428.   begin
  429.     Invalidate;
  430.     FNumGlyphs := Value;
  431.     GlyphChanged(Glyph);
  432.   end;
  433. end;
  434.  
  435. function TButtonGlyph.CreateButtonGlyph(State: TButtonState): Integer;
  436. const
  437.   ROP_DSPDxax = $00E20746;
  438. var
  439.   TmpImage, DDB, MonoBmp: TBitmap;
  440.   IWidth, IHeight: Integer;
  441.   IRect, ORect: TRect;
  442.   I: TButtonState;
  443.   DestDC: HDC;
  444. begin
  445.   if (State = bsDown) and (NumGlyphs < 3) then State := bsUp;
  446.   Result := FIndexs[State];
  447.   if Result <> -1 then Exit;
  448.   if (FOriginal.Width or FOriginal.Height) = 0 then Exit;
  449.   IWidth := FOriginal.Width div FNumGlyphs;
  450.   IHeight := FOriginal.Height;
  451.   if FGlyphList = nil then
  452.   begin
  453.     if GlyphCache = nil then GlyphCache := TGlyphCache.Create;
  454.     FGlyphList := GlyphCache.GetList(IWidth, IHeight);
  455.   end;
  456.   TmpImage := TBitmap.Create;
  457.   try
  458.     TmpImage.Width := IWidth;
  459.     TmpImage.Height := IHeight;
  460.     IRect := Rect(0, 0, IWidth, IHeight);
  461.     TmpImage.Canvas.Brush.Color := clBtnFace;
  462.     TmpImage.Palette := CopyPalette(FOriginal.Palette);
  463.     I := State;
  464.     if Ord(I) >= NumGlyphs then I := bsUp;
  465.     ORect := Rect(Ord(I) * IWidth, 0, (Ord(I) + 1) * IWidth, IHeight);
  466.     case State of
  467.       bsUp, bsDown,
  468.       bsExclusive:
  469.         begin
  470.           TmpImage.Canvas.CopyRect(IRect, FOriginal.Canvas, ORect);
  471.           if FOriginal.TransparentMode = tmFixed then
  472.             FIndexs[State] := FGlyphList.AddMasked(TmpImage, FTransparentColor)
  473.           else
  474.             FIndexs[State] := FGlyphList.AddMasked(TmpImage, clDefault);
  475.         end;
  476.       bsDisabled:
  477.         begin
  478.           MonoBmp := nil;
  479.           DDB := nil;
  480.           try
  481.             MonoBmp := TBitmap.Create;
  482.             DDB := TBitmap.Create;
  483.             DDB.Assign(FOriginal);
  484.             DDB.HandleType := bmDDB;
  485.             if NumGlyphs > 1 then
  486.             with TmpImage.Canvas do
  487.             begin    { Change white & gray to clBtnHighlight and clBtnShadow }
  488.               CopyRect(IRect, DDB.Canvas, ORect);
  489.               MonoBmp.Monochrome := True;
  490.               MonoBmp.Width := IWidth;
  491.               MonoBmp.Height := IHeight;
  492.  
  493.               { Convert white to clBtnHighlight }
  494.               DDB.Canvas.Brush.Color := clWhite;
  495.               MonoBmp.Canvas.CopyRect(IRect, DDB.Canvas, ORect);
  496.               Brush.Color := clBtnHighlight;
  497.               DestDC := Handle;
  498.               SetTextColor(DestDC, clBlack);
  499.               SetBkColor(DestDC, clWhite);
  500.               BitBlt(DestDC, 0, 0, IWidth, IHeight,
  501.                      MonoBmp.Canvas.Handle, 0, 0, ROP_DSPDxax);
  502.  
  503.               { Convert gray to clBtnShadow }
  504.               DDB.Canvas.Brush.Color := clGray;
  505.               MonoBmp.Canvas.CopyRect(IRect, DDB.Canvas, ORect);
  506.               Brush.Color := clBtnShadow;
  507.               DestDC := Handle;
  508.               SetTextColor(DestDC, clBlack);
  509.               SetBkColor(DestDC, clWhite);
  510.               BitBlt(DestDC, 0, 0, IWidth, IHeight,
  511.                      MonoBmp.Canvas.Handle, 0, 0, ROP_DSPDxax);
  512.  
  513.               { Convert transparent color to clBtnFace }
  514.               DDB.Canvas.Brush.Color := ColorToRGB(FTransparentColor);
  515.               MonoBmp.Canvas.CopyRect(IRect, DDB.Canvas, ORect);
  516.               Brush.Color := clBtnFace;
  517.               DestDC := Handle;
  518.               SetTextColor(DestDC, clBlack);
  519.               SetBkColor(DestDC, clWhite);
  520.               BitBlt(DestDC, 0, 0, IWidth, IHeight,
  521.                      MonoBmp.Canvas.Handle, 0, 0, ROP_DSPDxax);
  522.             end
  523.             else
  524.             begin
  525.               { Create a disabled version }
  526.               with MonoBmp do
  527.               begin
  528.                 Assign(FOriginal);
  529.                 HandleType := bmDDB;
  530.                 Canvas.Brush.Color := clBlack;
  531.                 Width := IWidth;
  532.                 if Monochrome then
  533.                 begin
  534.                   Canvas.Font.Color := clWhite;
  535.                   Monochrome := False;
  536.                   Canvas.Brush.Color := clWhite;
  537.                 end;
  538.                 Monochrome := True;
  539.               end;
  540.               with TmpImage.Canvas do
  541.               begin
  542.                 Brush.Color := clBtnFace;
  543.                 FillRect(IRect);
  544.                 Brush.Color := clBtnHighlight;
  545.                 SetTextColor(Handle, clBlack);
  546.                 SetBkColor(Handle, clWhite);
  547.                 BitBlt(Handle, 1, 1, IWidth, IHeight,
  548.                   MonoBmp.Canvas.Handle, 0, 0, ROP_DSPDxax);
  549.                 Brush.Color := clBtnShadow;
  550.                 SetTextColor(Handle, clBlack);
  551.                 SetBkColor(Handle, clWhite);
  552.                 BitBlt(Handle, 0, 0, IWidth, IHeight,
  553.                   MonoBmp.Canvas.Handle, 0, 0, ROP_DSPDxax);
  554.               end;
  555.             end;
  556.           finally
  557.             DDB.Free;
  558.             MonoBmp.Free;
  559.           end;
  560.           FIndexs[State] := FGlyphList.AddMasked(TmpImage, clDefault);
  561.         end;
  562.     end;
  563.   finally
  564.     TmpImage.Free;
  565.   end;
  566.   Result := FIndexs[State];
  567.   FOriginal.Dormant;
  568. end;
  569.  
  570. procedure TButtonGlyph.DrawButtonGlyph(Canvas: TCanvas; const GlyphPos: TPoint;
  571.   State: TButtonState; Transparent: Boolean);
  572. var
  573.   Index: Integer;
  574. begin
  575.   if FOriginal = nil then Exit;
  576.   if (FOriginal.Width = 0) or (FOriginal.Height = 0) then Exit;
  577.   Index := CreateButtonGlyph(State);
  578.   with GlyphPos do
  579.     if Transparent or (State = bsExclusive) then
  580.       ImageList_DrawEx(FGlyphList.Handle, Index, Canvas.Handle, X, Y, 0, 0,
  581.         clNone, clNone, ILD_Transparent)
  582.     else
  583.       ImageList_DrawEx(FGlyphList.Handle, Index, Canvas.Handle, X, Y, 0, 0,
  584.         clNone, clNone, ILD_Transparent);
  585. end;
  586.  
  587. procedure TButtonGlyph.DrawButtonText(Canvas: TCanvas; const Caption: string;
  588.   TextBounds: TRect; State: TButtonState);
  589. begin
  590.   with Canvas do
  591.   begin
  592.     Brush.Style := bsClear;
  593.     if State = bsDisabled then
  594.     begin
  595.       OffsetRect(TextBounds, 1, 1);
  596.       Font.Color := clBtnHighlight;
  597.       DrawText(Handle, PChar(Caption), Length(Caption), TextBounds, 0);
  598.       OffsetRect(TextBounds, -1, -1);
  599.       Font.Color := clBtnShadow;
  600.       DrawText(Handle, PChar(Caption), Length(Caption), TextBounds, 0);
  601.     end else
  602.       DrawText(Handle, PChar(Caption), Length(Caption), TextBounds,
  603.         DT_CENTER or DT_VCENTER or DT_SINGLELINE);
  604.   end;
  605. end;
  606.  
  607. procedure TButtonGlyph.CalcButtonLayout(Canvas: TCanvas; const Client: TRect;
  608.     const Offset: TPoint; const Caption: string; Layout: TButtonLayout; Margin,
  609.     Spacing: Integer; var GlyphPos: TPoint; var TextBounds: TRect);
  610. var
  611.     TextPos: TPoint;
  612.     ClientSize, GlyphSize, TextSize: TPoint;
  613.     TotalSize: TPoint;
  614. begin
  615.     { calculate the item sizes }
  616.     ClientSize := Point(Client.Right - Client.Left, Client.Bottom -
  617.         Client.Top);
  618.  
  619.     if FOriginal <> nil then
  620.         GlyphSize := Point(FOriginal.Width div FNumGlyphs, FOriginal.Height) else
  621.         GlyphSize := Point(0, 0);
  622.  
  623.     if Length(Caption) > 0 then
  624.     begin
  625.         TextBounds := Rect(0, 0, Client.Right - Client.Left, 0);
  626.         DrawText(Canvas.Handle, PChar(Caption), Length(Caption), TextBounds, DT_CALCRECT);
  627.         TextSize := Point(TextBounds.Right - TextBounds.Left, TextBounds.Bottom -
  628.             TextBounds.Top);
  629.     end
  630.     else
  631.     begin
  632.         TextBounds := Rect(0, 0, 0, 0);
  633.         TextSize := Point(0,0);
  634.     end;
  635.  
  636.     if Layout in [blGlyphLeft, blGlyphRight] then
  637.     begin
  638.         GlyphPos.Y := 0;
  639.         TextPos.Y := (ClientSize.Y - TextSize.Y + 1) div 2;
  640.     end
  641.     else
  642.     begin
  643.         GlyphPos.X := 0;
  644.         TextPos.X := (ClientSize.X - TextSize.X + 1) div 2;
  645.     end;
  646.  
  647.   { if there is no text or no bitmap, then Spacing is irrelevant }
  648.   if (TextSize.X = 0) or (GlyphSize.X = 0) then
  649.     Spacing := 0;
  650.  
  651.   { adjust Margin and Spacing }
  652.   if Margin = -1 then
  653.   begin
  654.     if Spacing = -1 then
  655.     begin
  656.       TotalSize := Point(GlyphSize.X + TextSize.X, GlyphSize.Y + TextSize.Y);
  657.       if Layout in [blGlyphLeft, blGlyphRight] then
  658.         Margin := (ClientSize.X - TotalSize.X) div 3
  659.       else
  660.         Margin := (ClientSize.Y - TotalSize.Y) div 3;
  661.       Spacing := Margin;
  662.     end
  663.     else
  664.     begin
  665.       TotalSize := Point(GlyphSize.X + Spacing + TextSize.X, GlyphSize.Y +
  666.         Spacing + TextSize.Y);
  667.       if Layout in [blGlyphLeft, blGlyphRight] then
  668.         Margin := (ClientSize.X - TotalSize.X + 1) div 2
  669.       else
  670.         Margin := (ClientSize.Y - TotalSize.Y + 1) div 2;
  671.     end;
  672.   end
  673.   else
  674.   begin
  675.     if Spacing = -1 then
  676.     begin
  677.       TotalSize := Point(ClientSize.X - (Margin + GlyphSize.X), ClientSize.Y -
  678.         (Margin + GlyphSize.Y));
  679.       if Layout in [blGlyphLeft, blGlyphRight] then
  680.         Spacing := (TotalSize.X - TextSize.X) div 2
  681.       else
  682.         Spacing := (TotalSize.Y - TextSize.Y) div 2;
  683.     end;
  684.   end;
  685.  
  686.   case Layout of
  687.     blGlyphLeft:
  688.       begin
  689.         GlyphPos.X := Margin;
  690.         TextPos.X := GlyphPos.X + GlyphSize.X + Spacing;
  691.       end;
  692.     blGlyphRight:
  693.       begin
  694.         GlyphPos.X := ClientSize.X - Margin - GlyphSize.X;
  695.         TextPos.X := GlyphPos.X - Spacing - TextSize.X;
  696.       end;
  697.     blGlyphTop:
  698.       begin
  699.         GlyphPos.Y := Margin;
  700.         TextPos.Y := GlyphPos.Y + GlyphSize.Y + Spacing;
  701.       end;
  702.     blGlyphBottom:
  703.       begin
  704.         GlyphPos.Y := ClientSize.Y - Margin - GlyphSize.Y;
  705.         TextPos.Y := GlyphPos.Y - Spacing - TextSize.Y;
  706.       end;
  707.   end;
  708.  
  709.   { fixup the result variables }
  710.   with GlyphPos do
  711.   begin
  712.     Inc(X, Client.Left + Offset.X);
  713.     Inc(Y, Client.Top + Offset.Y);
  714.   end;
  715.   OffsetRect(TextBounds, TextPos.X + Client.Left + Offset.X,
  716.     TextPos.Y + Client.Top + Offset.X);
  717. end;
  718.  
  719. function TButtonGlyph.Draw(Canvas: TCanvas; const Client: TRect;
  720.   const Offset: TPoint; const Caption: string; Layout: TButtonLayout;
  721.   Margin, Spacing: Integer; State: TButtonState; Transparent: Boolean): TRect;
  722. var
  723.   GlyphPos: TPoint;
  724. begin
  725.   CalcButtonLayout(Canvas, Client, Offset, Caption, Layout, Margin, Spacing,
  726.     GlyphPos, Result);
  727.   DrawButtonGlyph(Canvas, GlyphPos, State, Transparent);
  728.   DrawButtonText(Canvas, Caption, Result, State);
  729. end;
  730.  
  731. { TCoolButton }
  732.  
  733. constructor TCoolButton.Create(AOwner: TComponent);
  734. begin
  735.     inherited Create(AOwner);
  736.     SetBounds(0, 0, 25, 25);
  737.     ControlStyle := [csCaptureMouse, csOpaque, csDoubleClicks];
  738.     FGlyph := TButtonGlyph.Create;
  739.     TButtonGlyph(FGlyph).OnChange := GlyphChanged;
  740.     ParentFont := True;
  741.     FSpacing := 4;
  742.     FMargin := -1;
  743.     FLayout := blGlyphLeft;
  744.     Inc(ButtonCount);
  745.     numglyphs:=4;
  746. end;
  747.  
  748. destructor TCoolButton.Destroy;
  749. begin
  750.   TButtonGlyph(FGlyph).Free;
  751.   Dec(ButtonCount);
  752.   if ButtonCount = 0 then
  753.   begin
  754.     Pattern.Free;
  755.     Pattern := nil;
  756.   end;
  757.     inherited Destroy;
  758. end;
  759.  
  760. procedure TCoolButton.Paint;
  761. const
  762.     DownStyles: array[Boolean] of Integer = (BDR_RAISEDINNER, BDR_SUNKENOUTER);
  763.     FillStyles: array[Boolean] of Integer = (0, 0);
  764. var
  765.     PaintRect: TRect;
  766.     Offset: TPoint;
  767. begin
  768. {    if not Enabled then
  769.     begin
  770.         FState := bsDisabled;
  771.         FDragging := False;
  772.     end
  773.     else if FState = bsDisabled then
  774.     begin
  775.         if FDown and (GroupIndex <> 0) then
  776.             FState := bsExclusive
  777.         else
  778.             FState := bsUp;
  779.     end else
  780.     if FState<>bsDown then if FMouseIncontrol then FState:=bsExclusive else FState:=bsUp;
  781.  }
  782.  if Enabled then
  783.  begin
  784.         if FMouseInControl then 
  785.         begin
  786.             if FState<>bsDown then FState:=bsExclusive;
  787.         end else Fstate:=bsUp;
  788.  end else FState:=bsDisabled;
  789.     Canvas.Font := Self.Font;
  790.     PaintRect := Rect(0, 0, Width, Height);
  791.         if (FState in [bsDown, bsExclusive]) or
  792.             (FMouseInControl and (FState <> bsDisabled)) or
  793.             (csDesigning in ComponentState) then
  794.             if csDesigning in ComponentState then DrawEdge(Canvas.Handle, PaintRect, DownStyles[FState in [bsDown, bsExclusive]],
  795.                 FillStyles[true] or BF_RECT);
  796. if FState in [bsDown, bsExclusive] then
  797.     begin
  798.         if (FState = bsExclusive) and (not FMouseInControl) then
  799.         begin
  800. //            if Pattern = nil then CreateBrushPattern;
  801. //            Canvas.Brush.Bitmap := Pattern;
  802. //     Canvas.FillRect(PaintRect);
  803.     end;
  804.     Offset.X := 0;
  805.     Offset.Y := 0;
  806.   end
  807.   else
  808.   begin
  809.     Offset.X := 0;
  810.     Offset.Y := 0;
  811.   end;
  812.   TButtonGlyph(FGlyph).Draw(Canvas, PaintRect, Offset, Caption, FLayout, FMargin,
  813.     FSpacing, FState,true);
  814. end;
  815.  
  816. procedure TCoolButton.UpdateTracking;
  817. var
  818.     P: TPoint;
  819. begin
  820. if Enabled then
  821.         begin
  822.             GetCursorPos(P);
  823.             FMouseInControl := not (FindDragTarget(P, True) = Self);
  824.             if FMouseInControl then
  825.                 Perform(CM_MOUSELEAVE, 0, 0);
  826.             end;
  827. end;
  828.  
  829. procedure TCoolButton.Loaded;
  830. var
  831.   State: TButtonState;
  832. begin
  833.   inherited Loaded;
  834.   if Enabled then
  835.     State := bsUp
  836.   else
  837.     State := bsDisabled;
  838.   TButtonGlyph(FGlyph).CreateButtonGlyph(State);
  839. end;
  840.  
  841. procedure TCoolButton.MouseDown(Button: TMouseButton; Shift: TShiftState;
  842.   X, Y: Integer);
  843. begin
  844.   inherited MouseDown(Button, Shift, X, Y);
  845.   if (Button = mbLeft) and Enabled then
  846.   begin
  847.     if not FDown then
  848.     begin
  849.       FState := bsDown;
  850.       Invalidate;
  851.     end;
  852.     FDragging := True;
  853.   end;
  854. end;
  855.  
  856. procedure TCoolButton.MouseMove(Shift: TShiftState; X, Y: Integer);
  857. var
  858.     NewState    : TButtonState;
  859.     P, P2            : TPoint;
  860.     OldState    : Boolean;
  861.     
  862. begin
  863.     OldState := FMouseInControl;
  864.     GetCursorPos(P);
  865.     P2 := ScreenToClient (P);
  866.     FMouseInControl := (TButtonGlyph(FGlyph).Glyph.Canvas.Pixels[P2.x, P2.y] <> TButtonGlyph(FGlyph).Glyph.Canvas.Pixels[0, Glyph.Height - 1]) and
  867.                                          (P2.X < Glyph.Width) and (P2.Y < Glyph.Height) and (FindDragTarget(P, True) = Self);
  868.     inherited MouseMove(Shift, X, Y);
  869.     if FDragging then
  870.     begin
  871.         if not FDown then NewState := bsUp
  872.         else NewState := bsExclusive;
  873.         if (X >= 0) and (X < ClientWidth) and (Y >= 0) and (Y <= ClientHeight) then
  874.             if FDown then NewState := bsExclusive else NewState := bsDown;
  875.         if (NewState <> FState) then
  876.         begin
  877.             FState := NewState;
  878.         end;
  879.     end;
  880.     If (OldState <> FMouseInControl) then Invalidate;
  881. end;
  882.  
  883. procedure TCoolButton.MouseUp(Button: TMouseButton; Shift: TShiftState;
  884.   X, Y: Integer);
  885. var
  886.   DoClick: Boolean;
  887. begin
  888.   inherited MouseUp(Button, Shift, X, Y);
  889.   if FDragging then
  890.   begin
  891.     FDragging := False;
  892.     DoClick := (X >= 0) and (X < ClientWidth) and (Y >= 0) and (Y <= ClientHeight);
  893.     if FGroupIndex = 0 then
  894.     begin
  895.       { Redraw face in-case mouse is captured }
  896.       FState := bsUp;
  897.       FMouseInControl := False;
  898.       if DoClick and not (FState in [bsExclusive, bsDown]) then
  899.         Invalidate;
  900.     end
  901.     else
  902.       if DoClick then
  903.       begin
  904.         SetDown(not FDown);
  905.         if FDown then Repaint;
  906.       end
  907.       else
  908.       begin
  909.         if FDown then FState := bsExclusive;
  910.         Repaint;
  911.       end;
  912.     if DoClick then Click;
  913.     UpdateTracking;
  914.   end;
  915. end;
  916.  
  917. procedure TCoolButton.Click;
  918. begin
  919.   inherited Click;
  920. end;
  921.  
  922. function TCoolButton.GetPalette: HPALETTE;
  923. begin
  924.   Result := Glyph.Palette;
  925. end;
  926.  
  927. function TCoolButton.GetGlyph: TBitmap;
  928. begin
  929.   Result := TButtonGlyph(FGlyph).Glyph;
  930. end;
  931.  
  932. procedure TCoolButton.SetGlyph(Value: TBitmap);
  933. begin
  934.   TButtonGlyph(FGlyph).Glyph := Value;
  935.   Invalidate;
  936. end;
  937.  
  938. function TCoolButton.GetNumGlyphs: TNumGlyphs;
  939. begin
  940.   Result := TButtonGlyph(FGlyph).NumGlyphs;
  941. end;
  942.  
  943. procedure TCoolButton.SetNumGlyphs(Value: TNumGlyphs);
  944. begin
  945.   if Value < 0 then Value := 1
  946.   else if Value > 4 then Value := 4;
  947.   if Value <> TButtonGlyph(FGlyph).NumGlyphs then
  948.   begin
  949.     TButtonGlyph(FGlyph).NumGlyphs := Value;
  950.     Invalidate;
  951.   end;
  952. end;
  953.  
  954. procedure TCoolButton.GlyphChanged(Sender: TObject);
  955. begin
  956.   Invalidate;
  957. end;
  958.  
  959. procedure TCoolButton.UpdateExclusive;
  960. var
  961.   Msg: TMessage;
  962. begin
  963.   if (FGroupIndex <> 0) and (Parent <> nil) then
  964.   begin
  965.     Msg.Msg := CM_BUTTONPRESSED;
  966.     Msg.WParam := FGroupIndex;
  967.     Msg.LParam := Longint(Self);
  968.     Msg.Result := 0;
  969.     Parent.Broadcast(Msg);
  970.   end;
  971. end;
  972.  
  973. procedure TCoolButton.SetDown(Value: Boolean);
  974. begin
  975.   if FGroupIndex = 0 then Value := False;
  976.   if Value <> FDown then
  977.   begin
  978.     if FDown and (not FAllowAllUp) then Exit;
  979.     FDown := Value;
  980.     if Value then
  981.     begin
  982.       if FState = bsUp then Invalidate;
  983.       FState := bsExclusive
  984.     end
  985.     else
  986.     begin
  987.       FState := bsUp;
  988.       Repaint;
  989.     end;
  990.     if Value then UpdateExclusive;
  991.   end;
  992. end;
  993.  
  994. procedure TCoolButton.SetGroupIndex(Value: Integer);
  995. begin
  996.   if FGroupIndex <> Value then
  997.   begin
  998.     FGroupIndex := Value;
  999.     UpdateExclusive;
  1000.   end;
  1001. end;
  1002.  
  1003. procedure TCoolButton.SetLayout(Value: TButtonLayout);
  1004. begin
  1005.   if FLayout <> Value then
  1006.   begin
  1007.     FLayout := Value;
  1008.     Invalidate;
  1009.   end;
  1010. end;
  1011.  
  1012. procedure TCoolButton.SetMargin(Value: Integer);
  1013. begin
  1014.   if (Value <> FMargin) and (Value >= -1) then
  1015.   begin
  1016.     FMargin := Value;
  1017.     Invalidate;
  1018.   end;
  1019. end;
  1020.  
  1021. procedure TCoolButton.SetSpacing(Value: Integer);
  1022. begin
  1023.   if Value <> FSpacing then
  1024.   begin
  1025.     FSpacing := Value;
  1026.     Invalidate;
  1027.   end;
  1028. end;
  1029.  
  1030. procedure TCoolButton.SetAllowAllUp(Value: Boolean);
  1031. begin
  1032.   if FAllowAllUp <> Value then
  1033.   begin
  1034.     FAllowAllUp := Value;
  1035.     UpdateExclusive;
  1036.   end;
  1037. end;
  1038.  
  1039. procedure TCoolButton.WMLButtonDblClk(var Message: TWMLButtonDown);
  1040. begin
  1041.   inherited;
  1042.   if FDown then DblClick;
  1043. end;
  1044.  
  1045. procedure TCoolButton.CMEnabledChanged(var Message: TMessage);
  1046. const
  1047.   NewState: array[Boolean] of TButtonState = (bsDisabled, bsUp);
  1048. begin
  1049.   TButtonGlyph(FGlyph).CreateButtonGlyph(NewState[Enabled]);
  1050.   UpdateTracking;
  1051.   Repaint;
  1052. end;
  1053.  
  1054. procedure TCoolButton.CMButtonPressed(var Message: TMessage);
  1055. var
  1056.   Sender: TCoolButton;
  1057. begin
  1058.   if Message.WParam = FGroupIndex then
  1059.   begin
  1060.     Sender := TCoolButton(Message.LParam);
  1061.     if Sender <> Self then
  1062.     begin
  1063.       if Sender.Down and FDown then
  1064.       begin
  1065.         FDown := False;
  1066.         FState := bsUp;
  1067.         Invalidate;
  1068.       end;
  1069.       FAllowAllUp := Sender.AllowAllUp;
  1070.     end;
  1071.   end;
  1072. end;
  1073.  
  1074. procedure TCoolButton.CMDialogChar(var Message: TCMDialogChar);
  1075. begin
  1076.   with Message do
  1077.     if IsAccel(CharCode, Caption) and Enabled then
  1078.     begin
  1079.       Click;
  1080.       Result := 1;
  1081.     end else
  1082.       inherited;
  1083. end;
  1084.  
  1085. procedure TCoolButton.CMFontChanged(var Message: TMessage);
  1086. begin
  1087.   Invalidate;
  1088. end;
  1089.  
  1090. procedure TCoolButton.CMTextChanged(var Message: TMessage);
  1091. begin
  1092.   Invalidate;
  1093. end;
  1094.  
  1095. procedure TCoolButton.CMSysColorChange(var Message: TMessage);
  1096. begin
  1097.   with TButtonGlyph(FGlyph) do
  1098.   begin
  1099.     Invalidate;
  1100.     CreateButtonGlyph(FState);
  1101.   end;
  1102. end;
  1103.  
  1104. procedure TCoolButton.WMEraseBkgnd( var message:TWMEraseBkgnd);
  1105. begin
  1106.     message.Result:=0;
  1107. end;
  1108.  
  1109.  
  1110.  
  1111. procedure TCoolButton.CMMouseLeave(var Message: TMessage);
  1112. begin
  1113.     inherited;
  1114.     if FMouseInControl and Enabled and not FDragging then
  1115.     begin
  1116.         FMouseInControl := False;
  1117.         Invalidate;
  1118.     end;
  1119. end;
  1120.  
  1121. procedure TCoolButton.WMPaint( var message:TWMPaint);
  1122. begin
  1123.     Paint;
  1124.   message.Result:=0;
  1125. end;
  1126. procedure TCoolButton.WMNCPaint( var message:TWMNCPaint);
  1127. begin
  1128.     Paint;
  1129.   message.Result:=0;
  1130. end;
  1131.  
  1132.  
  1133. end.
  1134.