{ From: jmillard@nmsu.edu (James B. Millard) : Howdy...yet another wacky question here. Anyone have any sample code to : create a custom object? Like a spin button, for example. Can you create a : button in resource workshop that's not a borland style button OR a windows : style button....i.e. A windows style button with a bitmap on it. If you : can tell me, I'll be your friend forever. :) Here is an "interface" unit and a "spin" control dll, written in pascal. I have not included the RW interface functions, because I haven't written them yet (and I may not). Let me say a little bit about the control. It is probably not like the normal spin control in that it only includes the up and down buttons. It was written to provide an addon spin control to an existing edit object I already have. In the resource workshop, create a window next to the edit you want to have the spin control for, rename the class to "JBM_SpinButton". Then have the edit control send a message to the spin control before the inherited setup window is called. This message is the WM_SPINSETEDIT shown below, the WPARAM should be the HWindow of the edit control. This associates the edit control with the spin control - the spin control will ensure it is properly aligned. When the spin button gets a mouse click it sends the edit control a WM_SPINUP or WM_SPINDOWN message as appropriate, whatever you do with it is up to you. (It doesn't have to be an edit window for that matter...) At the very least, it provides a working example of a control created in TP. (Note the bitmaps are included after the spin.dll file... as spin.rc) If you have any questions, just email me. ******************************************************************************* File: spinlib.pas ******************************************************************************* } Unit SpinLib; Interface Uses WinTypes, WinProcs; {* messages used by spin control and edit control} Const WM_SPINSETEDIT = WM_USER + 500; WM_SPINUP = WM_USER + 501; WM_SPINDOWN = WM_USER + 502; {* State flags} STATE_NOSPIN = $0000; STATE_SPINUP = $0001; STATE_SPINDOWN = $0002; STATE_CAPTURE = $0010; {* record used by spin control} Type PSpinStruct = ^TSpinStruct; TSpinStruct = Record hwndNotify : HWnd; wState : Word; Count : Integer; End; Procedure LoadSpinLibrary; Implementation Var hSpin : THandle; OldExitProc : Pointer; {* procedure to load "spin.dll"} Procedure LoadSpinLibrary; Begin hSpin:=LoadLibrary('spin.dll'); End; {* new exit procedure to automatically unload "spin.dll"} Procedure SpinExit; Far; Begin ExitProc:=OldExitProc; If (hSpin<>0) Then FreeLibrary(hSpin); End; Begin OldExitProc:=ExitProc; ExitProc:=@SpinExit; hSpin:=0; End. ******************************************************************************* File: spin.pas ******************************************************************************* { Windows's "spin" control by Brad Millard, JBM, P.O. Box 3648, University Park, NM 88003 There is no warranty, no license, or restrictions, use at your own risk, feel free to distribute... If you find any bugs, please let me know... jmillard@nmsu.edu } Library SpinCtl; {$R SPIN.RES} Uses WinProcs, WinTypes, Win31, SpinLib; Const ID_SPINTIMER = 1000; {***** Spin Button painting procedure *****} Procedure Paint(AWnd : HWnd; DC : HDC; SS : PSpinStruct); Var bm, obm : HBitMap; mdc : HDC; tbm : TBitMap; Begin {* Get state and load the correct bitmap} Case (SS^.wState AND $000F) Of STATE_NOSPIN : bm:=LoadBitMap(HInstance, 'SPIN_NOSPIN'); STATE_SPINUP : bm:=LoadBitMap(HInstance, 'SPIN_UP'); STATE_SPINDOWN : bm:=LoadBitMap(HInstance, 'SPIN_DOWN'); End; {* If the bitmap is OK, then paint} If (bm<>0) Then Begin GetObject(bm, SizeOf(TBitMap), @tbm); mdc:=CreateCompatibleDC(DC); obm:=SelectObject(mdc, bm); BitBlt(DC, 0, 0, tbm.bmWidth, tbm.bmHeight, mdc, 0, 0, SRCCOPY); SelectObject(mdc, obm); DeleteObject(bm); DeleteDC(mdc); End; End; {* function GetState returns the state constant depending on mouse hit position} Function GetState(AWnd : HWnd; WParam : Word; LParam : LongInt; SS : PSpinStruct) : Word; Var r : TRect; Begin GetClientRect(AWnd, r); If (PtInRect(r, TPoint(LParam))) Then Begin If (TPoint(LParam).Y>12) Then GetState:=STATE_SPINDOWN Else GetState:=STATE_SPINUP; End Else GetState:=0; End; {* notifies the edit control (hwndNotify) and increments the notification count} Function NotifyWnd(AWnd : HWnd; SS : PSpinStruct) : LongInt; Begin NotifyWnd:=0; If (SS<>NIL) Then With SS^ Do Begin Case (wState AND $000F) Of STATE_SPINUP : NotifyWnd:=SendMessage(hwndNotify, WM_SPINUP, AWnd, Count); STATE_SPINDOWN : NotifyWnd:=SendMessage(hwndNotify, WM_SPINDOWN, AWnd, Count); End; Inc(Count); End; End; {***** Message Specific Procedures *****} {* paints the button in the current state} Function wmPaint(AWnd : HWnd; SS : PSpinStruct) : LongInt; Var PS : TPaintStruct; Begin BeginPaint(AWnd, PS); Paint(AWnd, PS.hDC, SS); EndPaint(AWnd, PS); wmPaint:=0; End; {* sets focus to hwndNotify, sets capture, notifies hwndNotify of the first hit, and resets the count, also starts timer for continuous mouse hits, then paints the new state} Function wmLButtonDown(AWnd : HWnd; WParam : Word; LParam : LongInt) : LongInt; Var dc : HDC; ss : PSpinStruct; Begin ss:=PSpinStruct(GetWindowLong(AWnd, 0)); If (ss<>NIL) Then With ss^ Do Begin SetFocus(hwndNotify); SetCapture(AWnd); wState:=STATE_CAPTURE OR GetState(AWnd, WParam, LParam, ss); NotifyWnd(AWnd, ss); Count:=-1; SetTimer(AWnd, ID_SPINTIMER, 40, NIL); dc:=GetDC(AWnd); Paint(AWnd, dc, ss); ReleaseDC(AWnd, dc); End; wmLButtonDown:=0; End; {* if the button has set capture, paints the button in the correct state} Function wmMouseMove(AWnd : HWnd; WParam : Word; LParam : LongInt) : LongInt; Var dc : HDC; ss : PSpinStruct; r : TRect; ns : Word; Begin ss:=PSpinStruct(GetWindowLong(AWnd, 0)); If (ss<>NIL) Then With ss^ Do Begin If (wState AND STATE_CAPTURE = STATE_CAPTURE) Then Begin ns:=GetState(AWnd, WParam, LParam, ss); {* only paint if we have to...} If (ns<>wState AND $000F) Then Begin wState:=STATE_CAPTURE OR ns; dc:=GetDC(AWnd); Paint(AWnd, dc, ss); ReleaseDC(AWnd, dc); End; End; End; wmMouseMove:=0; End; {* Cleans up -- releases capture, repaints, and kills the timer} Function wmLButtonUp(AWnd : HWnd; WParam : Word; LParam : LongInt) : LongInt; Var dc : HDC; ss : PSpinStruct; Begin ss:=PSpinStruct(GetWindowLong(AWnd, 0)); If (ss<>NIL) Then With ss^ Do Begin If (wState AND STATE_CAPTURE = STATE_CAPTURE) Then Begin KillTimer(AWnd, ID_SPINTIMER); wState:=0; dc:=GetDC(AWnd); Paint(AWnd, dc, ss); ReleaseDC(AWnd, dc); ReleaseCapture; End; End; wmLButtonUp:=0; End; {* Responds to timer messages when capturing. Counts negative for 12 counts. When the count is negative, the hwndNotify is not "spun" (except for the initial hit). When the count reaches -13, it is reset to 0 and then hwndNotify is spun on each timer message. This provides the delay after the first mouse click} Function wmTimer(AWnd : HWnd; WParam : Word; LParam : LongInt) : LongInt; Var ss : PSpinStruct; Begin ss:=PSpinStruct(GetWindowLong(AWnd, 0)); With ss^ Do Begin If (wState AND STATE_CAPTURE <> 0) AND (hwndNotify<>0) Then Begin If (Count<0) Then Dec(Count); If (Count<-12) OR (Count>0) Then Begin Count:=0; NotifyWnd(AWnd, ss); End; End; End; wmTimer:=0; End; {* This message is recieved from the edit control before either are shown. The spin control calculates it's correct position and moves itself accordingly.} Function wmSpinSetEdit(AWnd : HWnd; WParam : Word; LParam : LongInt) : LongInt; Var ss : PSpinStruct; r : TRect; x, y : Integer; Begin ss:=PSpinStruct(GetWindowLong(AWnd, 0)); If (ss<>NIL) Then With ss^ Do Begin hwndNotify:=WParam; GetWindowRect(hwndNotify, r); MapWindowPoints(0, GetParent(AWnd), r, 2); x:=r.Right + 6; y:=r.Top + (r.Bottom - r.Top - 23) DIV 2 - 1; MoveWindow(AWnd, x, y, 17, 23, False); End; wmSpinSetEdit:=0; End; {***** Spin control Window Procedure *****} Function SpinWndProc(AWnd : HWnd; Msg, WParam : Word; LParam : LongInt) : LongInt; Export; Var SpinStruct : PSpinStruct; Begin SpinWndProc:=0; Case Msg Of WM_CREATE : Begin {* Create the TSpinStruct and set it at the extra bytes} SpinStruct:=PSpinStruct(GetWindowLong(AWnd, 0)); GetMem(SpinStruct, SizeOf(TSpinStruct)); If (SpinStruct<>NIL) Then SpinStruct^.wState:=0; SetWindowLong(AWnd, 0, LongInt(SpinStruct)); End; WM_DESTROY : Begin {* Get rid of TSpinStruct} SpinStruct:=PSpinStruct(GetWindowLong(AWnd, 0)); If (SpinStruct<>NIL) Then FreeMem(SpinStruct, SizeOf(TSpinStruct)); End; WM_PAINT : Begin SpinStruct:=PSpinStruct(GetWindowLong(AWnd, 0)); If (SpinStruct<>NIL) Then SpinWndProc:=wmPaint(AWnd, SpinStruct); End; WM_GETDLGCODE : SpinWndProc:=DLGC_BUTTON; WM_SPINSETEDIT : SpinWndProc:=wmSpinSetEdit(AWnd, WParam, LParam); WM_LBUTTONDOWN : SpinWndProc:=wmLButtonDown(AWnd, WParam, LParam); WM_MOUSEMOVE : SpinWndProc:=wmMouseMove(AWnd, WParam, LParam); WM_LBUTTONUP : SpinWndProc:=wmLButtonUp(AWnd, WParam, LParam); WM_TIMER : SpinWndProc:=wmTimer(AWnd, WParam, LParam); Else SpinWndProc:=DefWindowProc(AWnd, Msg, WParam, LParam); End; End; {***** Class Registration Procedure *****} Procedure RegisterSpinClass; Var w : TWndClass; Begin w.cbClsExtra := 0; {* reserve extra space for TSpinStruct} w.cbWndExtra := SizeOf(Pointer); {* this is the DLL instance, I guess..., it works...} w.hInstance := System.hInstance; w.hIcon := 0; w.hCursor := LoadCursor(0, idc_Arrow); {* Since we use a bitmap that covers the whole window, don't mess with the background.} w.hbrBackground := 0; w.lpszMenuName := NIL; w.lpszClassName := 'JBM_SpinButton'; w.style := cs_HRedraw or cs_VRedraw or cs_GlobalClass; w.lpfnWndProc := @SpinWndProc; RegisterClass(w); End; Exports SpinWndProc; Begin {* register the class when the library is loaded} RegisterSpinClass; End. ******************************************************************************* File: spin.rc ******************************************************************************* SPIN_DOWN BITMAP BEGIN '42 4D 8A 01 00 00 00 00 00 00 76 00 00 00 28 00' '00 00 11 00 00 00 17 00 00 00 01 00 04 00 00 00' '00 00 14 01 00 00 00 00 00 00 00 00 00 00 00 00' '00 00 10 00 00 00 00 00 00 00 00 00 BF 00 00 BF' '00 00 00 BF BF 00 BF 00 00 00 BF 00 BF 00 BF BF' '00 00 C0 C0 C0 00 80 80 80 00 00 00 FF 00 00 FF' '00 00 00 FF FF 00 FF 00 00 00 FF 00 FF 00 FF FF' '00 00 FF FF FF 00 00 00 00 00 00 00 00 00 00 00' '00 00 08 77 77 77 77 77 77 77 00 00 00 00 08 77' '77 77 77 77 77 77 0F 00 00 00 08 77 77 77 77 77' '77 77 00 00 00 00 08 77 77 77 70 77 77 77 00 00' '00 00 08 77 77 77 00 07 77 77 00 00 00 00 08 77' '77 70 00 00 77 77 00 00 00 00 08 77 77 00 00 00' '07 77 00 00 00 00 08 77 77 77 77 77 77 77 00 00' '00 00 08 77 77 77 77 77 77 77 00 00 00 00 08 88' '88 88 88 88 88 88 00 00 00 00 07 70 00 00 00 00' '00 88 00 00 00 00 0F F7 77 77 77 77 77 88 00 00' '00 00 0F F7 77 77 77 77 77 88 00 00 00 00 0F F7' '70 00 00 00 77 88 00 00 00 00 0F F7 77 00 00 07' '77 88 0F 00 00 00 0F F7 77 70 00 77 77 88 00 00' '00 00 0F F7 77 77 07 77 77 88 00 00 00 00 0F F7' '77 77 77 77 77 88 00 00 00 00 0F F7 77 77 77 77' '77 88 00 00 00 00 0F FF FF FF FF FF FF F8 00 00' '00 00 0F FF FF FF FF FF FF FF 00 00 00 00 00 00' '00 00 00 00 00 00 00 00 00 00' END SPIN_NOSPIN BITMAP BEGIN '42 4D 8A 01 00 00 00 00 00 00 76 00 00 00 28 00' '00 00 11 00 00 00 17 00 00 00 01 00 04 00 00 00' '00 00 14 01 00 00 00 00 00 00 00 00 00 00 00 00' '00 00 10 00 00 00 00 00 00 00 00 00 BF 00 00 BF' '00 00 00 BF BF 00 BF 00 00 00 BF 00 BF 00 BF BF' '00 00 C0 C0 C0 00 80 80 80 00 00 00 FF 00 00 FF' '00 00 00 FF FF 00 FF 00 00 00 FF 00 FF 00 FF FF' '00 00 FF FF FF 00 00 00 00 00 00 00 00 00 00 00' '00 00 0F 88 88 88 88 88 88 88 00 00 00 00 0F F8' '88 88 88 88 88 88 0F 00 00 00 0F F7 77 77 77 77' '77 88 00 00 00 00 0F F7 77 77 77 77 77 88 00 00' '00 00 0F F7 77 77 07 77 77 88 00 00 00 00 0F F7' '77 70 00 77 77 88 00 00 00 00 0F F7 77 00 00 07' '77 88 00 00 00 00 0F F7 70 00 00 00 77 88 00 00' '00 00 0F F7 77 77 77 77 77 88 00 00 00 00 0F F7' '77 77 77 77 77 88 00 00 00 00 07 70 00 00 00 00' '00 88 00 00 00 00 0F F7 77 77 77 77 77 88 00 00' '00 00 0F F7 77 77 77 77 77 88 00 00 00 00 0F F7' '70 00 00 00 77 88 00 00 00 00 0F F7 77 00 00 07' '77 88 0F 00 00 00 0F F7 77 70 00 77 77 88 00 00' '00 00 0F F7 77 77 07 77 77 88 00 00 00 00 0F F7' '77 77 77 77 77 88 00 00 00 00 0F F7 77 77 77 77' '77 88 00 00 00 00 0F FF FF FF FF FF FF F8 00 00' '00 00 0F FF FF FF FF FF FF FF 00 00 00 00 00 00' '00 00 00 00 00 00 00 00 00 00' END SPIN_UP BITMAP BEGIN '42 4D 8A 01 00 00 00 00 00 00 76 00 00 00 28 00' '00 00 11 00 00 00 17 00 00 00 01 00 04 00 00 00' '00 00 14 01 00 00 00 00 00 00 00 00 00 00 00 00' '00 00 10 00 00 00 00 00 00 00 00 00 BF 00 00 BF' '00 00 00 BF BF 00 BF 00 00 00 BF 00 BF 00 BF BF' '00 00 C0 C0 C0 00 80 80 80 00 00 00 FF 00 00 FF' '00 00 00 FF FF 00 FF 00 00 00 FF 00 FF 00 FF FF' '00 00 FF FF FF 00 00 00 00 00 00 00 00 00 00 00' '00 00 0F 88 88 88 88 88 88 88 00 00 00 00 0F F8' '88 88 88 88 88 88 01 00 00 00 0F F7 77 77 77 77' '77 88 00 00 00 00 0F F7 77 77 77 77 77 88 00 00' '00 00 0F F7 77 77 07 77 77 88 00 00 00 00 0F F7' '77 70 00 77 77 88 00 00 00 00 0F F7 77 00 00 07' '77 88 00 00 00 00 0F F7 70 00 00 00 77 88 00 00' '00 00 0F F7 77 77 77 77 77 88 00 00 00 00 0F F7' '77 77 77 77 77 88 00 00 00 00 08 88 88 88 88 88' '88 88 00 00 00 00 08 77 77 77 77 77 77 77 00 00' '00 00 08 77 77 00 00 00 07 77 00 00 00 00 08 77' '77 70 00 00 77 77 00 00 00 00 08 77 77 77 00 07' '77 77 01 00 00 00 08 77 77 77 70 77 77 77 00 00' '00 00 08 77 77 77 77 77 77 77 00 00 00 00 08 77' '77 77 77 77 77 77 00 00 00 00 08 77 77 77 77 77' '77 77 00 00 00 00 08 77 77 77 77 77 77 77 00 00' '00 00 08 88 88 88 88 88 88 88 00 00 00 00 00 00' '00 00 00 00 00 00 00 00 00 00' END