Page 1 of 2

CustomButtons sourcecode released

Posted: Wed Nov 21, 2007 2:06 am
by netmaestro
Current version: 1.1 November 24, 2007

Changes this version:

-bug fixed which caused CustomButtons to malfunction when instances were attached to multiple windows in the application

Test prog button images and help .chm: http://www.greatlakescode.com/custombuttonfiles.zip

Code: Select all

;============================================================
; Program:          CustomButton.pbi
; Author:           Network Maestro
; Date:             October 21,2006
; Target OS:        Microsoft Windows All
; Target Compiler:  PureBasic 4.10
; License:          Free, unrestricted, credit appreciated
;                   but not required                   
;============================================================

#Enter           = #WM_APP+$100
#Leave           = #WM_APP+$101
#CB_Button_Click = #WM_APP+$102
#CB_SolidBrush   = -2

Global lastbutton

Procedure Monitor(hwnd)
  Protected cp1.POINT
  Protected cp2.POINT
  
  Repeat
    If IsWindowEnabled_(hwnd)
      If GetProp_(hwnd,"cb_status") = 2
        SetProp_(hwnd,"cb_status",0)
        If IsImage(GetProp_(hwnd,"cb_disab"))
          SetGadgetState(GetDlgCtrlID_(hwnd),ImageID(GetProp_(hwnd,"cb_cold")))
        EndIf
      EndIf
      hRgn = GetProp_(hwnd,"cb_region")
      GetCursorPos_(@cp1)
      GetCursorPos_(@cp2)
      MapWindowPoints_(0,hwnd,@cp1,1)
      If WindowFromPoint_(cp2\x,cp2\y) = hwnd
        If PtInRegion_(hRgn,cp1\x,cp1\y)
          If GetProp_(hwnd,"cb_status") = 0  
            SetProp_(hwnd,"cb_status", 1)
            If lastbutton = 0 Or lastbutton = hwnd Or GetAsyncKeyState_(#VK_LBUTTON) & 32768 = 0
              PostMessage_(hwnd,#Enter,0,0)
              lastbutton = 0
            EndIf
          EndIf
        Else
          If GetProp_(hwnd,"cb_status") = 1 
            SetProp_(hwnd,"cb_status", 0)
            PostMessage_(hwnd,#Leave,0,0)
            If GetAsyncKeyState_(#VK_LBUTTON) & 32768
              If lastbutton = 0
                lastbutton = hwnd
              EndIf
            Else
              lastbutton = 0
            EndIf
          EndIf
        EndIf
      Else
        If GetProp_(hwnd,"cb_status") = 1 
          SetProp_(hwnd,"cb_status", 0)
          PostMessage_(hwnd,#Leave,0,0)
          If GetAsyncKeyState_(#VK_LBUTTON) & 32768
            If lastbutton = 0
              lastbutton = hwnd
            EndIf
          Else
            lastbutton = 0
          EndIf
        EndIf
      EndIf      
    Else
      If IsWindow_(hwnd)
        If IsWindowEnabled_(hwnd)=0
          SetProp_(hwnd,"cb_status",2)
          If IsImage(GetProp_(hwnd,"cb_disab"))
            SetGadgetState(GetDlgCtrlID_(hwnd),ImageID(GetProp_(hwnd,"cb_disab")))
          EndIf
        EndIf  
      EndIf  
    EndIf
    Delay(10)
  ForEver
EndProcedure

Procedure CustomButtonProc(hwnd, msg, wparam, lparam)
  Protected cp1.POINT 
  Protected oldproc = GetProp_(hwnd,"cb_oldproc")
  Protected monitor_tid = GetProp_(hwnd, "cb_monitor_tid")
  hRgn = GetProp_(hwnd, "cb_region")
  GetCursorPos_(@cp1)
  MapWindowPoints_(0,hwnd,@cp1,1)    
  Select msg
    Case #WM_NCDESTROY
      If IsThread(monitor_tid)
        KillThread(monitor_tid)
        WaitThread(monitor_tid)
      EndIf
      RemoveProp_(hwnd, "cb_status")
      RemoveProp_(hwnd, "cb_disab")
      RemoveProp_(hwnd, "cb_cold")
      RemoveProp_(hwnd, "cb_warm")
      RemoveProp_(hwnd, "cb_hot")
      RemoveProp_(hwnd, "cb_parent")
      RemoveProp_(hwnd, "cb_oldproc")
      RemoveProp_(hwnd, "cb_region")
      RemoveProp_(hwnd, "cb_monitor_tid")
    Case #WM_LBUTTONDOWN
      If PtInRegion_(hRgn,cp1\x,cp1\y)  
        SetGadgetState(GetDlgCtrlID_(hwnd),ImageID(GetProp_(hwnd,"cb_hot")))
      EndIf
    Case #WM_LBUTTONUP
      If PtInRegion_(hRgn,cp1\x,cp1\y)  
        SetGadgetState(GetDlgCtrlID_(hwnd),ImageID(GetProp_(hwnd,"cb_warm")))
        If lastbutton = 0 Or lastbutton= hwnd
          PostMessage_(GetProp_(hwnd,"cb_parent"), #CB_Button_Click, GetDlgCtrlID_(hwnd), 0)
        EndIf
        lastbutton = 0 
      EndIf
    Case #Enter
      If GetAsyncKeyState_(#VK_LBUTTON) & 32768
        SetGadgetState(GetDlgCtrlID_(hwnd),ImageID(GetProp_(hwnd,"cb_hot")))
      Else
        SetGadgetState(GetDlgCtrlID_(hwnd),ImageID(GetProp_(hwnd,"cb_warm")))
      EndIf
    Case #Leave
      SetGadgetState(GetDlgCtrlID_(hwnd),ImageID(GetProp_(hwnd,"cb_cold")))
  EndSelect 
  ProcedureReturn CallWindowProc_(oldproc,hwnd,msg,wparam,lparam)
EndProcedure 

Procedure TransBlit(hdc, hBitmap, xStart, yStart, TransColor) 
  ; by netmaestro based on http://support.microsoft.com/kb/79212/EN-US/ 
  Define.POINT  ptSize 
  hdcTemp = CreateCompatibleDC_(hdc) 
  SelectObject_(hdcTemp, hBitmap) 
  GetObject_(hBitmap, SizeOf(BITMAP), bm.BITMAP) 
  ptSize\x = bm\bmWidth 
  ptSize\y = bm\bmHeight 
  DPtoLP_(hdcTemp, @ptSize, 1) 
  hdcBack   = CreateCompatibleDC_(hdc) 
  hdcObject = CreateCompatibleDC_(hdc) 
  hdcMem    = CreateCompatibleDC_(hdc) 
  hdcSave   = CreateCompatibleDC_(hdc) 
  bmAndBack   = CreateBitmap_(ptSize\x, ptSize\y, 1, 1, #Null) 
  bmAndObject = CreateBitmap_(ptSize\x, ptSize\y, 1, 1, #Null) 
  bmAndMem    = CreateCompatibleBitmap_(hdc, ptSize\x, ptSize\y) 
  bmSave      = CreateCompatibleBitmap_(hdc, ptSize\x, ptSize\y) 
  bmBackOld   = SelectObject_(hdcBack, bmAndBack) 
  bmObjectOld = SelectObject_(hdcObject, bmAndObject) 
  bmMemOld    = SelectObject_(hdcMem, bmAndMem) 
  bmSaveOld   = SelectObject_(hdcSave, bmSave) 
  SetMapMode_(hdcTemp, GetMapMode_(hdc)) 
  BitBlt_(hdcSave, 0, 0, ptSize\x, ptSize\y, hdcTemp, 0, 0, #SRCCOPY) 
  cColor = SetBkColor_(hdcTemp, TransColor) 
  BitBlt_(hdcObject, 0, 0, ptSize\x, ptSize\y, hdcTemp, 0, 0, #SRCCOPY) 
  SetBkColor_(hdcTemp, cColor) 
  BitBlt_(hdcBack, 0, 0, ptSize\x, ptSize\y, hdcObject, 0, 0, #NOTSRCCOPY) 
  BitBlt_(hdcMem, 0, 0, ptSize\x, ptSize\y, hdc, xStart, yStart, #SRCCOPY) 
  BitBlt_(hdcMem, 0, 0, ptSize\x, ptSize\y, hdcObject, 0, 0, #SRCAND) 
  BitBlt_(hdcTemp, 0, 0, ptSize\x, ptSize\y, hdcBack, 0, 0, #SRCAND) 
  BitBlt_(hdcMem, 0, 0, ptSize\x, ptSize\y, hdcTemp, 0, 0, #SRCPAINT) 
  BitBlt_(hdc, xStart, yStart, ptSize\x, ptSize\y, hdcMem, 0, 0, #SRCCOPY) 
  BitBlt_(hdcTemp, 0, 0, ptSize\x, ptSize\y, hdcSave, 0, 0, #SRCCOPY) 
  DeleteObject_(SelectObject_(hdcBack, bmBackOld)) 
  DeleteObject_(SelectObject_(hdcObject, bmObjectOld)) 
  DeleteObject_(SelectObject_(hdcMem, bmMemOld)) 
  DeleteObject_(SelectObject_(hdcSave, bmSaveOld)) 
  DeleteDC_(hdcMem) 
  DeleteDC_(hdcBack) 
  DeleteDC_(hdcObject) 
  DeleteDC_(hdcSave) 
  DeleteDC_(hdcTemp) 
EndProcedure 

ProcedureDLL GrabRegion(ImageID, transcolor) ; HBITMAP ImageID, COLORREF transcolor 

  ;======================================================= 
  ;                                                      = 
  ;      Very fast bitmap -> region creator              = 
  ;                                                      = 
  ;      Version 1.0 Release                             = 
  ;                                                      = 
  ;      By netmaestro                                   = 
  ;                                                      = 
  ;      Contributors: eesau, nico, flype, rescator      = 
  ;                                                      = 
  ;      June 26, 2007                                   = 
  ;                                                      = 
  ;======================================================= 
  
  Structure RECTARRAY 
    rect.RECT[0] 
  EndStructure 

  Protected bmp.BITMAP,width.l,height.l,hVisibleRgn.l,combineresult.l=0, returnvalue.l = 0 
  Protected BmiInfo.BITMAPINFOHEADER,rowbytes.l,*ColorBits,hDC.l,iRes.l,Structure_Max.l 
  Protected *Buffer.RGNDATAHEADER,*rd.RECTARRAY,rectcount.l,y.l,x.l,pxcount.l,*px.LONG 
  Protected transcount.l,firsttrans.l,regionSize.l,hTransparentRgn.l 

  If GetObject_(ImageID, SizeOf(BITMAP), @bmp.BITMAP) And bmp 
    width = bmp\bmWidth 
    height = bmp\bmHeight 
    hVisibleRgn=CreateRectRgn_(0, 0, width, height) 
    If hVisibleRgn 
      With BmiInfo 
        \biSize         = SizeOf(BITMAPINFOHEADER) 
        \biWidth        = width 
        \biHeight       = -height 
        \biPlanes       = 1 
        \biBitCount     = 32 
        \biCompression  = #BI_RGB 
      EndWith    
      rowbytes =  SizeOf(LONG)*width 
      *ColorBits = AllocateMemory(rowbytes*height) 
      If *ColorBits 
        hDC   = GetWindowDC_(#Null) 
        If hDC 
          iRes  = GetDIBits_(hDC, ImageID, 0, height, *ColorBits, @bmiInfo, #DIB_RGB_COLORS) 
          If iRes 
            ReleaseDC_(#Null, hDC) 
            Structure_Max=(width*height*16)+SizeOf(RGNDATAHEADER) 
            *Buffer=AllocateMemory(Structure_Max) 
            If *Buffer 
              *rd=*Buffer+SizeOf(RGNDATAHEADER) 
              rectcount = 0 
              For y=0 To height-1 
                pxcount=0 
                For x=0 To rowbytes-1 Step 4 
                  *px = *ColorBits + rowbytes * y + x 
                  If *px\l = transcolor 
                    transcount = 1 : firsttrans = pxcount 
                    x+SizeOf(LONG) : *px.LONG = *ColorBits + rowbytes * y + x 
                    While *px\l = transcolor And x <= rowbytes-1 
                      transcount+1 : pxcount+1 : x+SizeOf(LONG) 
                      *px = *ColorBits + rowbytes * y + x 
                    Wend 
                    x-SizeOf(LONG) : *px.LONG = *ColorBits + rowbytes * y + x 
                    With *rd\rect[rectcount] 
                      \left   = firsttrans            
                      \top    = y                      
                      \right  = firsttrans+transcount 
                      \bottom = y+1      
                    EndWith 
                    rectcount+1 
                  EndIf 
                  pxcount+1 
                Next 
              Next 
              With *Buffer 
                \dwSize         = SizeOf(RGNDATAHEADER) 
                \iType          = #RDH_RECTANGLES 
                \nCount         = rectcount 
                \nRgnSize       = rectcount * SizeOf(RECT) 
                \rcBound\left   = 0 
                \rcBound\top    = 0 
                \rcBound\right  = width 
                \rcBound\bottom = height 
              EndWith 
              RegionSize=SizeOf(RGNDATAHEADER)+(rectcount * SizeOf(RECT)) 
              hTransparentRgn = ExtCreateRegion_(0, RegionSize, *Buffer) 
              If hTransparentRgn 
                combineresult = CombineRgn_(hVisibleRgn, hVisibleRgn, hTransparentRgn, #RGN_XOR) 
                If combineresult = #SIMPLEREGION Or combineresult = #COMPLEXREGION 
                  returnvalue = hVisibleRgn 
                Else 
                  returnvalue = 0 
                EndIf 
                DeleteObject_(hTransparentRgn) 
              EndIf 
              FreeMemory(*Buffer) 
            EndIf 
          EndIf 
        EndIf 
        FreeMemory(*ColorBits) 
      EndIf 
    EndIf 
    DeleteObject_(bmp) 
  EndIf 
  ProcedureReturn returnvalue 
EndProcedure 

ProcedureDLL CustomButton(parent, button, x, y, w, h, image, bkgimage, tcolr) 
  disab=GrabImage(image,#PB_Any, 0,   0, w, h)
  cold=GrabImage(image,#PB_Any, w,   0, w, h)
  warm=GrabImage(image, #PB_Any, w*2, 0, w, h)
  hot=GrabImage(image, #PB_Any, w*3, 0, w, h)  
  
  ; Define clipping region
  If w*5 = ImageWidth(image)
    mask=GrabImage(image, #PB_Any, w*4, 0, w, h) 
    region = GrabRegion(ImageID(mask), #Black)
  EndIf
  If Not region
    region = CreateRectRgn_(0,0,w,h)
  EndIf
  
  ; Apply transparency if requested
  If IsImage(bkgimage)
    tmp1=GrabImage(bkgimage, #PB_Any, x, y, w, h)
    tmp2=CreateImage(#PB_Any, w, h)
    dc = StartDrawing(ImageOutput(tmp2))
      DrawImage(ImageID(tmp1),0,0)
      TransBlit(dc,ImageID(disab),0,0,tcolr)
    StopDrawing()
    StartDrawing(ImageOutput(disab))
      DrawImage(ImageID(tmp2),0,0)
    StopDrawing()
    dc = StartDrawing(ImageOutput(tmp2))
      DrawImage(ImageID(tmp1),0,0)
      TransBlit(dc,ImageID(cold),0,0,tcolr)
    StopDrawing()
    StartDrawing(ImageOutput(cold))
      DrawImage(ImageID(tmp2),0,0)
    StopDrawing()
    dc = StartDrawing(ImageOutput(tmp2))
      DrawImage(ImageID(tmp1),0,0)
      TransBlit(dc,ImageID(warm),0,0,tcolr)
    StopDrawing()
    StartDrawing(ImageOutput(warm))
      DrawImage(ImageID(tmp2),0,0)
    StopDrawing()
    dc = StartDrawing(ImageOutput(tmp2))
      DrawImage(ImageID(tmp1),0,0)
      TransBlit(dc,ImageID(hot),0,0,tcolr)
    StopDrawing()
    StartDrawing(ImageOutput(hot))
      DrawImage(ImageID(tmp2),0,0)
    StopDrawing()
    FreeImage(tmp1)
    FreeImage(tmp2)
  EndIf
  
  CustomButtonID = ImageGadget(button,x,y,w,h,ImageID(cold))
  If button=#PB_Any 
    handle = GadgetID(CustomButtonID)
  Else
    handle = CustomButtonID
  EndIf

  old = SetWindowLong_(handle, #GWL_WNDPROC, @CustomButtonProc())
  
  SetProp_(handle,"cb_oldproc", old)
  SetProp_(handle,"cb_status",0)
  SetProp_(handle,"cb_disab",disab)  
  SetProp_(handle,"cb_cold",cold)
  SetProp_(handle,"cb_warm",warm)
  SetProp_(handle,"cb_hot",hot)
  SetProp_(handle,"cb_parent",parent)
  SetProp_(handle,"cb_region",region)
  
  monitor_tid = CreateThread(@Monitor(),handle)
  SetProp_(handle,"cb_monitor_tid",monitor_tid)
  
  ProcedureReturn CustomButtonID
  
EndProcedure

ProcedureDLL.s CustomButton_GetVersion()
  ProcedureReturn "Release 1.1"
EndProcedure

Code: Select all

;=================================
; test1.pb: Solid-color window
;=================================

IncludeFile "custombutton.pbi"
LoadImage(0,"buttons2.bmp")

Transcolor = RGB(224,223,227) ; Background color of buttons2.bmp
windowcolor=GetSysColor_(#COLOR_BTNFACE) ; Background color of the window

OpenWindow(0,0,0,300,200,"",$C80001) 
CreateGadgetList(WindowID(0)) 

mygadget = CustomButton(WindowID(0),#PB_Any,40,20,60,60,0,#CB_SolidBrush,Transcolor)
CustomButton(WindowID(0),1,120,20,60,60,0,#CB_SolidBrush,Transcolor)
CustomButton(WindowID(0),2,200,20,60,60,0,#CB_SolidBrush,Transcolor)
DisableGadget(mygadget,1)

Repeat 
  EventID=WaitWindowEvent() 
    If EventID = #CB_Button_Click   ; This is how you check for button clicks
      If EventwParam()=1            ; You don't use #PB_Event_Gadget, EventGadget()
        DisableGadget(mygadget,0)   ; or EventType(). They don't apply to 
      ElseIf EventwParam()=2        ; CustomButtons.
        DisableGadget(mygadget,1)   
      EndIf
    EndIf
Until EventID=#WM_CLOSE 

Code: Select all

;===============================================
; test2.pb: Image-background or skinned window
;===============================================
IncludeFile "custombutton.pbi"

LoadImage(0,"buttons.bmp")

CreateImage(33,300,200)                 ;========================================
StartDrawing(ImageOutput(33))           ; Image to use for window background
  For i = 3 To 229                      ; Included to demonstrate transparent
    Line(0,i-30,300,1,RGB(150,60,i))    ; capabilities of CustomButtons       
  Next                                  ; You can load an image or use the image
StopDrawing()                           ; you're skinning the window with
brush=CreatePatternBrush_(ImageID(33))  ; It must be the same size as the window
                                        ;========================================

Transcolor = RGB(64,64,64) ; Background color of buttons.bmp

OpenWindow(0,0,0,300,200,"",$CF0001) 
CreateGadgetList(WindowID(0)) 
CustomButton(WindowID(0),0,40,20,60,60,0,33,Transcolor)
CustomButton(WindowID(0),1,120,20,60,60,0,33,Transcolor)
CustomButton(WindowID(0),2,200,20,60,60,0,33,Transcolor)
DisableGadget(0,1)

SetClassLong_(WindowID(0),#GCL_HBRBACKGROUND,brush)
InvalidateRect_(WindowID(0),0,1)
Repeat 
  EventID=WaitWindowEvent() 
    If EventID = #CB_Button_Click
      If EventwParam()=1
        DisableGadget(0,0)
      ElseIf EventwParam()=2
        DisableGadget(0,1)
      EndIf
    EndIf
Until EventID=#WM_CLOSE 

DeleteObject_(brush)

Posted: Wed Nov 21, 2007 2:27 am
by Flaming Amoeba
Thank you a million times over Bossman!

Posted: Wed Nov 21, 2007 2:42 am
by rsts
WOW!

:shock:

cheers

Posted: Wed Nov 21, 2007 2:57 am
by netmaestro
I'm going to move away from compiled libs and post mostly source from here on. It's too much trouble to keep up with recompiling libs every time new versions of PB are released, and if people want to tailbite the code they can always do that. The whole point of a coding forum is to provide a place where people can learn new skills through the sharing of others, so source is the best way to do that.

Posted: Wed Nov 21, 2007 10:34 am
by Dare
Thank you.

Posted: Wed Nov 21, 2007 10:48 am
by Kwai chang caine
Too cool 8)
You are a very large frog :lol:

Posted: Wed Nov 21, 2007 11:30 am
by Rings
netmaestro wrote:I'm going to move away from compiled libs and post mostly source from here on. It's too much trouble to keep up with recompiling libs every time new versions of PB are released, and if people want to tailbite the code they can always do that. The whole point of a coding forum is to provide a place where people can learn new skills through the sharing of others, so source is the best way to do that.
thank you to share your code.
(same opinion as i have)

Posted: Wed Nov 21, 2007 12:24 pm
by Psychophanta
Ah! This is funny casual.
Just yesterday i was porting this your custombuttons stuff to 4.10 and was playing with it. And now you repost it!
Thanks!

Posted: Wed Nov 21, 2007 2:56 pm
by eJan
Yes sources...
Thanks a lot!

Re: CustomButtons sourcecode released

Posted: Wed Nov 21, 2007 3:18 pm
by NoahPhense
Very nice.. thanks for the share.

- np

Posted: Wed Nov 21, 2007 6:06 pm
by Micko
i like it netmaestro :D

Posted: Thu Nov 22, 2007 12:34 am
by kinglestat
really awesome
great work and much appreciated

Posted: Thu Nov 22, 2007 4:25 am
by Flaming Amoeba
We ought to all give Netmaestro a big wet sloppy kiss for releasing this. But hopefully he will settle for our ocular thanks.



Thank You, Netmaestro!

Posted: Sat Nov 24, 2007 2:12 am
by byo
Thanks a lot for that!
Really great!

Maybe someone can implement these sugestions (because I can't):

- Transparent background (rounded shapes);

- a #PB_Button_Toggle equivalent button type.

8)

Posted: Sat Nov 24, 2007 3:33 pm
by netmaestro
Bug fixed causing problems when added to multiple windows. Code updated in first post.