Advertisement
Guest User

WinAPI Icon Btn

a guest
May 6th, 2013
112
0
Never
Not a member of Pastebin yet? Sign Up, it unlocks many cool features!
Delphi 16.09 KB | None | 0 0
  1. unit winapi_iconbtn;
  2.  
  3. interface
  4.  
  5. uses
  6.   Windows, Messages;
  7.  
  8. type
  9.   TShiftState = set of (ssShift, ssAlt, ssCtrl, ssLeft, ssRight, ssMiddle, ssDouble);
  10.   TButtonLayout = (blGlyphLeft, blGlyphRight, blGlyphTop, blGlyphBottom);
  11.   TMouseButton = (mbLeft, mbRight, mbMiddle);
  12.   TMouseEvent = procedure(Sender : TObject; Button : TMouseButton;
  13.     Shift : TShiftState; X, Y : integer) of object;
  14.   TWndMethod = procedure(var Message : TMessage) of object;
  15.   TNotifyEvent = procedure(Sender : TObject) of object;
  16.   PObjectInstance = ^TObjectInstance;
  17.   TObjectInstance = packed record
  18.     Code : Byte;
  19.     Offset : integer;
  20.     case integer of
  21.       0 : (Next : PObjectInstance);
  22.       1 : (Method : TWndMethod);
  23.   end;
  24.   PInstanceBlock = ^TInstanceBlock;
  25.   TInstanceBlock = packed record
  26.     Next : PInstanceBlock;
  27.     Code : array[1..2] of Byte;
  28.     WndProcPtr : Pointer;
  29.     Instances : array[0..313] of TObjectInstance;
  30.   end;
  31.   TWAIconBtn = class(TObject)
  32.   private
  33.     FAppFont : HFONT;
  34.     FCaption : string;
  35.     FIconHandle : HICON;
  36.     FLayout : TButtonLayout;
  37.     FOnClick : TNotifyEvent;
  38.     FOnRightClick : TMouseEvent;
  39.     FEnabled, FVisible : boolean;
  40.     FHandle, FParentWindowHandle : HWND;
  41.     FIdent, FLeft, FTop, FWidth, FHeight : integer;
  42.     POldButtonProc, POldParentWindowProc : Pointer;
  43.     procedure SetTop(Value : integer);
  44.     procedure SetLeft(Value : integer);
  45.     procedure SetWidth(Value : integer);
  46.     procedure SetHeight(Value : integer);
  47.     procedure SetCaption(Value : string);
  48.     procedure SetEnabled(Value : boolean);
  49.     procedure SetVisible(Value : boolean);
  50.     procedure SetIconHandle(Value : HICON);
  51.     procedure DoRightClick(Sender : TObject);
  52.     procedure SetLayout(Value : TButtonLayout);
  53.     procedure WndProc(var AMessage : TMessage);
  54.     procedure RefreshStateOfTheControlByHidingAndShow;
  55.     procedure ParentWindowProc(var AMessage : TMessage);
  56.     procedure CalcButtonLayout(Canvas : HDC; const Client : TRect;
  57.       const IconToDraw : HICON; const CaptionOfButton : string;
  58.       const CaptionFont : HFONT; Layout : TButtonLayout;
  59.       Margin, Spacing : integer; var GlyphPos, TextPos : TPoint);
  60.     procedure DrawBitBtn(ParamDrawItem : PDrawItemStruct; BtnIconH : HICON; TextOfCaption : string);
  61.   public
  62.     procedure Click;
  63.     destructor Destroy; override;
  64.     constructor Create(ParentWindowHandle : HWND);
  65.   published
  66.     property Handle : HWND read FHandle;
  67.     property Top : integer read FTop write SetTop;
  68.     property Left : integer read FLeft write SetLeft;
  69.     property Width : integer read FWidth write SetWidth;
  70.     property Height : integer read FHeight write SetHeight;
  71.     property Caption : string read FCaption write SetCaption;
  72.     property Enabled : boolean read FEnabled write SetEnabled;
  73.     property Visible : boolean read FVisible write SetVisible;
  74.     property OnClick : TNotifyEvent read FOnClick write FOnClick;
  75.     property Layout : TButtonLayout read FLayout write SetLayout;
  76.     property IconHandle : HICON read FIconHandle write SetIconHandle;
  77.     property OnRightClick : TMouseEvent read FOnRightClick write FonRightClick;
  78.   end;
  79.  
  80. implementation
  81.  
  82. const
  83.   Btn_Margin = -1;
  84.   Btn_Spacing = 12;
  85.   Button_ClassName = 'Button';
  86.   WM_OWNERDRAW = WM_USER + 666;
  87.  
  88. var
  89.   InstBlockList : PInstanceBlock;
  90.   InstFreeList : PObjectInstance;
  91.  
  92. function Point(X, Y : integer) : TPoint;
  93. begin
  94.   Result.X := X;
  95.   Result.Y := Y;
  96. end;
  97.  
  98. function Rect(Left, Top, Right, Bottom : integer) : TRect;
  99. begin
  100.   Result.Left := Left;
  101.   Result.Top := Top;
  102.   Result.Bottom := Bottom;
  103.   Result.Right := Right;
  104. end;
  105.  
  106. function CalcJmpOffset(Src, Dest : Pointer) : Longint;
  107. begin
  108.   Result := Longint(Dest) - (Longint(Src) + 5);
  109. end;
  110.  
  111. function StdWndProc(Window : HWND; Message, WParam : Longint;
  112.   LParam : Longint) : Longint; stdcall; assembler;
  113. asm
  114.         XOR     EAX,EAX
  115.         PUSH    EAX
  116.         PUSH    LParam
  117.         PUSH    WParam
  118.         PUSH    Message
  119.         MOV     EDX,ESP
  120.         MOV     EAX,[ECX].Longint[4]
  121.         CALL    [ECX].Pointer
  122.         ADD     ESP,12
  123.         POP     EAX
  124. end;
  125.  
  126. function MakeObjectInstance(Method : TWndMethod) : Pointer;
  127. const
  128.   PageSize = 4096;
  129.   BlockCode : array[1..2] of Byte = ($59, $E9);
  130. var
  131.   Block : PInstanceBlock;
  132.   Instance : PObjectInstance;
  133. begin
  134.   if InstFreeList = nil then
  135.   begin
  136.     Block := VirtualAlloc(nil, PageSize, MEM_COMMIT, PAGE_EXECUTE_READWRITE);
  137.     Block^.Next := InstBlockList;
  138.     Move(BlockCode, Block^.Code, SizeOf(BlockCode));
  139.     Block^.WndProcPtr := Pointer(CalcJmpOffset(@Block^.Code[2], @StdWndProc));
  140.     Instance := @Block^.Instances;
  141.     repeat
  142.       Instance^.Code := $E8;
  143.       Instance^.Offset := CalcJmpOffset(Instance, @Block^.Code);
  144.       Instance^.Next := InstFreeList;
  145.       InstFreeList := Instance;
  146.       Inc(Longint(Instance), SizeOf(TObjectInstance));
  147.     until Longint(Instance) - Longint(Block) >= SizeOf(TInstanceBlock);
  148.     InstBlockList := Block;
  149.   end;
  150.   Result := InstFreeList;
  151.   Instance := InstFreeList;
  152.   InstFreeList := Instance^.Next;
  153.   Instance^.Method := Method;
  154. end;
  155.  
  156. function GetTextWidth(TextToCheck : string; GivenTextFonr : HFONT) : integer;
  157. var
  158.   DC : HDC;
  159.   PStr : PChar;
  160.   Size : TSize;
  161. begin
  162.   Result := 0;
  163.   if TextToCheck <> '' then
  164.   begin
  165.     GetMem(PStr, Length(TextToCheck));
  166.     CopyMemory(PStr, PChar(TextToCheck), Length(TextToCheck));
  167.     DC := GetDC(0);
  168.     SelectObject(DC, GivenTextFonr);
  169.     GetTextExtentPoint32(DC, PStr, Length(TextToCheck), Size);
  170.     ReleaseDC(0, DC);
  171.     FreeMem(PStr);
  172.     Result := Size.cx;
  173.   end;
  174. end;
  175.  
  176. function GetTextHeight(TextToCheck : string; GivenTextFonr : HFONT) : integer;
  177. var
  178.   DC : HDC;
  179.   PStr : PChar;
  180.   Size : TSize;
  181. begin
  182.   Result := 0;
  183.   if TextToCheck <> '' then
  184.   begin
  185.     GetMem(PStr, Length(TextToCheck));
  186.     CopyMemory(PStr, PChar(TextToCheck), Length(TextToCheck));
  187.     DC := GetDC(0);
  188.     SelectObject(DC, GivenTextFonr);
  189.     GetTextExtentPoint32(DC, PStr, Length(TextToCheck), Size);
  190.     ReleaseDC(0, DC);
  191.     FreeMem(PStr);
  192.     Result := Size.cy;
  193.   end;
  194. end;
  195.  
  196. procedure TWAIconBtn.RefreshStateOfTheControlByHidingAndShow;
  197. begin
  198.   ShowWindow(FHandle, SW_HIDE);
  199.   if FVisible then
  200.   begin
  201.     ShowWindow(FHandle, SW_SHOW);
  202.   end;
  203. end;
  204.  
  205. procedure TWAIconBtn.WndProc(var AMessage : TMessage);
  206. var
  207.   FDrawItem : PDrawItemStruct;
  208.  
  209. begin
  210.   with AMessage do
  211.   begin
  212.     case Msg of
  213.       WM_OWNERDRAW :
  214.         begin
  215.           FDrawItem := Pointer(LParam);
  216.           DrawBitBtn(FDrawItem, FIconHandle, FCaption);
  217.         end;
  218.       wM_SETCURSOR :
  219.         begin
  220.           if IsWindowEnabled(FHandle) then
  221.           begin
  222.             SetCursor(LoadCursor(0, IDC_HAND));
  223.             AMessage.Result := 0;
  224.             Exit;
  225.           end;
  226.         end;
  227.       WM_LBUTTONUP :
  228.         begin
  229.           if Assigned(FOnClick) then
  230.           begin
  231.             FOnClick(Self);
  232.           end;
  233.         end;
  234.       WM_RBUTTONUP :
  235.         begin
  236.           DoRightClick(Self);
  237.         end;
  238.     end;
  239.     if POldButtonProc <> nil then
  240.     begin
  241.       Result := CallWindowProc(POldButtonProc, Self.Handle, Msg, WParam, LParam);
  242.     end;
  243.   end;
  244. end;
  245.  
  246. procedure TWAIconBtn.ParentWindowProc(var AMessage : TMessage);
  247. var
  248.   FDrawItem : PDrawItemStruct;
  249. begin
  250.   with AMessage do
  251.   begin
  252.     case Msg of
  253.       WM_DRAWITEM :
  254.         begin
  255.           FDrawItem := Pointer(LParam);
  256.           if FDrawItem.hwndItem = Fhandle then
  257.           begin
  258.             SendMessage(FDrawItem.hwndItem, WM_OWNERDRAW, WParam, LParam);
  259.           end;
  260.         end
  261.     else
  262.       begin
  263.         Result := CallWindowProc(POldParentWindowProc, FParentWindowHandle, Msg, WParam, LParam);
  264.       end;
  265.     end;
  266.   end;
  267. end;
  268.  
  269. procedure TWAIconBtn.CalcButtonLayout(Canvas : HDC; const Client : TRect;
  270.   const IconToDraw : HICON; const CaptionOfButton : string;
  271.   const CaptionFont : HFONT; Layout : TButtonLayout;
  272.   Margin, Spacing : integer; var GlyphPos, TextPos : TPoint);
  273. var
  274.   TextBounds : TRect;
  275.   TotalSize : TPoint;
  276.   BMInfo : TagBITMAP;
  277.   IconInfo : TIconInfo;
  278.   ClientSize, GlyphSize, TextSize : TPoint;
  279. begin
  280.   if IconToDraw > 0 then
  281.   begin
  282.     if GetIconInfo(IconToDraw, IconInfo) then
  283.     begin
  284.       if GetObject(IconInfo.hbmColor, SizeOf(BMInfo), @BMInfo) > 0 then
  285.       begin
  286.         GlyphSize.X := BMInfo.bmWidth;
  287.         GlyphSize.Y := BMInfo.bmHeight;
  288.       end
  289.       else
  290.       begin
  291.         GlyphSize.X := 0;
  292.         GlyphSize.Y := 0;
  293.       end;
  294.     end;
  295.   end
  296.   else
  297.   begin
  298.     GlyphSize.X := 0;
  299.     GlyphSize.Y := 0;
  300.   end;
  301.   ClientSize := Point(Client.Right - Client.Left, Client.Bottom - Client.Top);
  302.   if Length(CaptionOfButton) > 0 then
  303.   begin
  304.     TextBounds := Rect(0, 0, Client.Right - Client.Left, 0);
  305.     DrawText(Canvas, PChar(CaptionOfButton), Length(CaptionOfButton), TextBounds, DT_CALCRECT);
  306.     TextSize.X := GetTextWidth(CaptionOfButton, CaptionFont);
  307.     TextSize.Y := GetTextHeight(CaptionOfButton, CaptionFont);
  308.   end
  309.   else
  310.   begin
  311.     TextBounds := Rect(0, 0, 0, 0);
  312.     TextSize := Point(0, 0);
  313.   end;
  314.   if Layout in [blGlyphLeft, blGlyphRight] then
  315.   begin
  316.     GlyphPos.Y := (ClientSize.Y - GlyphSize.Y + 1) div 2;
  317.     TextPos.Y := (ClientSize.Y - TextSize.Y + 1) div 2;
  318.   end
  319.   else
  320.   begin
  321.     GlyphPos.X := (ClientSize.X - GlyphSize.X + 1) div 2;
  322.     TextPos.X := (ClientSize.X - TextSize.X + 1) div 2;
  323.   end;
  324.   if (TextSize.X = 0) or (GlyphSize.X = 0) then
  325.   begin
  326.     Spacing := 0;
  327.   end;
  328.   if Margin = -1 then
  329.   begin
  330.     if Spacing = -1 then
  331.     begin
  332.       TotalSize := Point(GlyphSize.X + TextSize.X, GlyphSize.Y + TextSize.Y);
  333.       if Layout in [blGlyphLeft, blGlyphRight] then
  334.       begin
  335.         Margin := (ClientSize.X - TotalSize.X) div 3
  336.       end
  337.       else
  338.       begin
  339.         Margin := (ClientSize.Y - TotalSize.Y) div 3;
  340.       end;
  341.       Spacing := Margin;
  342.     end
  343.     else
  344.     begin
  345.       TotalSize := Point(GlyphSize.X + Spacing + TextSize.X, GlyphSize.Y +
  346.         Spacing + TextSize.Y);
  347.       if Layout in [blGlyphLeft, blGlyphRight] then
  348.       begin
  349.         Margin := (ClientSize.X - TotalSize.X + 1) div 2
  350.       end
  351.       else
  352.       begin
  353.         Margin := (ClientSize.Y - TotalSize.Y + 1) div 2;
  354.       end;
  355.     end;
  356.   end
  357.   else
  358.   begin
  359.     if Spacing = -1 then
  360.     begin
  361.       TotalSize := Point(ClientSize.X - (Margin + GlyphSize.X), ClientSize.Y -
  362.         (Margin + GlyphSize.Y));
  363.       if Layout in [blGlyphLeft, blGlyphRight] then
  364.       begin
  365.         Spacing := (TotalSize.X - TextSize.X) div 2
  366.       end
  367.       else
  368.       begin
  369.         Spacing := (TotalSize.Y - TextSize.Y) div 2;
  370.       end;
  371.     end;
  372.   end;
  373.   case Layout of
  374.     blGlyphLeft :
  375.       begin
  376.         GlyphPos.X := Margin;
  377.         TextPos.X := GlyphPos.X + GlyphSize.X + Spacing;
  378.       end;
  379.     blGlyphRight :
  380.       begin
  381.         GlyphPos.X := ClientSize.X - Margin - GlyphSize.X;
  382.         TextPos.X := GlyphPos.X - Spacing - TextSize.X;
  383.       end;
  384.     blGlyphTop :
  385.       begin
  386.         GlyphPos.Y := Margin;
  387.         TextPos.Y := GlyphPos.Y + GlyphSize.Y + Spacing;
  388.       end;
  389.     blGlyphBottom :
  390.       begin
  391.         GlyphPos.Y := ClientSize.Y - Margin - GlyphSize.Y;
  392.         TextPos.Y := GlyphPos.Y - Spacing - TextSize.Y;
  393.       end;
  394.   end;
  395. end;
  396.  
  397. procedure TWAIconBtn.DrawBitBtn(ParamDrawItem : PDrawItemStruct; BtnIconH : HICON; TextOfCaption : string);
  398. const
  399.   Icon_Param_Arr : array[boolean] of UINT = (0, DSS_DISABLED);
  400. var
  401.   OldFont : HFONT;
  402.   IconPos, TxtPos : TPoint;
  403. begin
  404.   CalcButtonLayout(ParamDrawItem.HDC, ParamDrawItem.rcItem, FIconHandle,
  405.     TextOfCaption, FAppFont, FLayout, Btn_Margin, Btn_Spacing, IconPos, TxtPos);
  406.   DrawState(ParamDrawItem.HDC, 0, nil, BtnIconH, 0,
  407.     ParamDrawItem.rcItem.Left + IconPos.X,
  408.     ParamDrawItem.rcItem.top + IconPos.Y, 0, 0, DST_ICON or
  409.     Icon_Param_Arr[ParamDrawItem.itemState = ODS_DISABLED]);
  410.   OldFont := SelectObject(ParamDrawItem.HDC, FAppFont);
  411.   if ParamDrawItem.itemState = ODS_DISABLED then
  412.   begin
  413.     SetTextColor(ParamDrawItem.HDC, GetSysColor(COLOR_BTNSHADOW));
  414.     if TextOfCaption <> '' then
  415.     begin
  416.       TextOut(ParamDrawItem.hDC, TxtPos.X, TxtPos.Y, PChar(TextOfCaption), Length(TextOfCaption));
  417.     end;
  418.   end
  419.   else
  420.   begin
  421.     if TextOfCaption <> '' then
  422.     begin
  423.       TextOut(ParamDrawItem.hDC, TxtPos.X, TxtPos.Y, PChar(TextOfCaption), Length(TextOfCaption));
  424.     end;
  425.   end;
  426.   if (ParamDrawItem.itemState and ODS_SELECTED) <> 0 then
  427.   begin
  428.     DrawEdge(ParamDrawItem.HDC, ParamDrawItem.rcItem, EDGE_SUNKEN, BF_RECT);
  429.   end
  430.   else
  431.   begin
  432.     DrawEdge(ParamDrawItem.HDC, ParamDrawItem.rcItem, EDGE_RAISED, BF_RECT);
  433.   end;
  434.   if ParamDrawItem.itemState = ODS_FOCUS then
  435.   begin
  436.     InflateRect(ParamDrawItem.rcItem, -4, -4);
  437.     DrawFocusRect(ParamDrawItem.HDC, ParamDrawItem.rcItem);
  438.   end;
  439.   SelectObject(ParamDrawItem.HDC, OldFont);
  440. end;
  441.  
  442. procedure TWAIconBtn.Click;
  443. begin
  444.   if Assigned(FOnClick) then
  445.   begin
  446.     FOnClick(Self);
  447.   end;
  448. end;
  449.  
  450. destructor TWAIconBtn.Destroy;
  451. begin
  452.   SetWindowLong(FParentWindowHandle, GWL_WNDPROC, Longint(POldParentWindowProc));
  453.   DestroyWindow(FHandle);
  454.   inherited Destroy;
  455. end;
  456.  
  457. constructor TWAIconBtn.Create(ParentWindowHandle : HWND);
  458. begin
  459.   FParentWindowHandle := ParentWindowHandle;
  460.   if IsWindow(FParentWindowHandle) then
  461.   begin
  462.     FTop := 4;
  463.     FLeft := 6;
  464.     FWidth := 140;
  465.     FHeight := 100;
  466.     FCaption := '';
  467.     FEnabled := True;
  468.     FVisible := True;
  469.     FIconHandle := 0;
  470.     FHandle := CreateWindowEx(0, Button_ClassName, '',
  471.       WS_CHILDWINDOW or WS_VISIBLE or WS_TABSTOP or BS_OWNERDRAW,
  472.       FLeft, FTop, FWidth, FHeight,
  473.       FParentWindowHandle, 0, HInstance, nil);
  474.     if FHandle > 0 then
  475.     begin
  476.       Randomize;
  477.       repeat
  478.         FIdent := Random(High(WORD) + 1);
  479.       until GetDlgItem(FParentWindowHandle, FIdent) <> FHandle;
  480.       SetWindowLong(FHandle, GWL_ID, FIdent);
  481.       POldButtonProc := Pointer(SetWindowLong(FHandle, GWL_WNDPROC, Longint(MakeObjectInstance(WndProc))));
  482.       POldParentWindowProc := Pointer(SetWindowLong(FParentWindowHandle, GWL_WNDPROC, Longint(MakeObjectInstance(ParentWindowProc))));
  483.     end;
  484.   end;
  485. end;
  486.  
  487. procedure TWAIconBtn.SetTop(Value : integer);
  488. begin
  489.   if FTop <> Value then
  490.   begin
  491.     FTop := Value;
  492.     SetWindowPos(FHandle, HWND_TOP, FLeft, FTop, FWidth, FHeight, SWP_NOZORDER);
  493.   end;
  494. end;
  495.  
  496. procedure TWAIconBtn.SetLeft(Value : integer);
  497. begin
  498.   if FLeft <> Value then
  499.   begin
  500.     FLeft := Value;
  501.     SetWindowPos(FHandle, HWND_TOP, FLeft, FTop, FWidth, FHeight, SWP_NOZORDER);
  502.   end;
  503. end;
  504.  
  505. procedure TWAIconBtn.SetWidth(Value : integer);
  506. begin
  507.   if FWidth <> Value then
  508.   begin
  509.     FWidth := Value;
  510.     SetWindowPos(FHandle, HWND_TOP, FLeft, FTop, FWidth, FHeight, SWP_NOZORDER);
  511.   end;
  512. end;
  513.  
  514. procedure TWAIconBtn.SetHeight(Value : integer);
  515. begin
  516.   if FWidth <> Value then
  517.   begin
  518.     FHeight := Value;
  519.     SetWindowPos(FHeight, HWND_TOP, FLeft, FTop, FWidth, FHeight, SWP_NOZORDER);
  520.   end;
  521. end;
  522.  
  523. procedure TWAIconBtn.SetCaption(Value : string);
  524. begin
  525.   if FCaption <> Value then
  526.   begin
  527.     FCaption := Value;
  528.     RefreshStateOfTheControlByHidingAndShow;
  529.   end;
  530. end;
  531.  
  532. procedure TWAIconBtn.SetEnabled(Value : boolean);
  533. begin
  534.   if FEnabled <> Value then
  535.   begin
  536.     FEnabled := Value;
  537.     EnableWindow(FHandle, FEnabled);
  538.   end;
  539. end;
  540.  
  541. procedure TWAIconBtn.SetVisible(Value : boolean);
  542. begin
  543.   if FVisible <> Value then
  544.   begin
  545.     FVisible := Value;
  546.     if FVisible then
  547.     begin
  548.       ShowWindow(FHandle, SW_SHOW)
  549.     end
  550.     else
  551.     begin
  552.       ShowWindow(FHandle, SW_HIDE);
  553.     end;
  554.   end;
  555. end;
  556.  
  557. procedure TWAIconBtn.SetIconHandle(Value : HICON);
  558. begin
  559.   if FIconHandle <> Value then
  560.   begin
  561.     FIconHandle := Value;
  562.     RefreshStateOfTheControlByHidingAndShow;
  563.   end;
  564. end;
  565.  
  566. procedure TWAIconBtn.DoRightClick(Sender : TObject);
  567. var
  568.   MouseCo : TPoint;
  569. begin
  570.   GetCursorPos(MouseCo);
  571.   if Assigned(FOnRightClick) then
  572.   begin
  573.     FOnRightClick(Self, mbRight, [], MouseCo.X, MouseCo.Y);
  574.   end;
  575. end;
  576.  
  577. procedure TWAIconBtn.SetLayout(Value : TButtonLayout);
  578. begin
  579.   if FLayout <> Value then
  580.   begin
  581.     FLayout := Value;
  582.     RefreshStateOfTheControlByHidingAndShow;
  583.   end;
  584. end;
  585.  
  586. end.
Advertisement
Add Comment
Please, Sign In to add comment
Advertisement