'Classes/ActiveBasic/Windows/UI/Control.ab

#require <Classes/ActiveBasic/Windows/UI/Forms/EventArgs.ab>

Namespace ActiveBasic
Namespace Windows
Namespace UI
Namespace Forms

Class Control
Public

'1

	Sub Control()
	End Sub

	Virtual Sub ~Control()
	End Sub

	Function Handle() As WindowHandle
		Handle = wnd
	End Function

	Static Function FromHWnd(hwnd As HWND) As Control
		FromHWnd = Nothing
		If IsWindow(hwnd) Then
			FromHWnd = FromHWndCore(hwnd)
		End If
	End Function

Private
	Static Function FromHWndCore(hwnd As HWND) As Control
		If GetClassLongPtr(hwnd, GCW_ATOM) = atom Then
			Dim gchValue = GetProp(hwnd, PropertyInstance As ULONG_PTR As PCTSTR) As ULONG_PTR
			If gchValue <> 0 Then
				Dim gch = System.Runtime.InteropServices.GCHandle.FromIntPtr(gchValue)
				FromHWndCore = gch.Target As Control
				Exit Function
			End If
		End If
	End Function

'--------------------------------
' 1 ウィンドウ作成
/*
	Function Create(
		parent As HWND,
		rect As RECT,
		name As String,
		style As DWord,
		exStyle = 0 As DWord,
		menu = 0 As HMENU) As HWND
*/
Public
	Function Create() As Boolean
		Dim cs As CREATESTRUCT
		cs.hInstance = hInstance
		cs.lpszClass = (atom As ULONG_PTR) As LPCTSTR
		GetCreateStruct(cs)
		Create = createImpl(cs)
	End Function

Protected
	Abstract Sub GetCreateStruct(ByRef cs As CREATESTRUCT)

	Function createImpl(ByRef cs As CREATESTRUCT) As Boolean
		Imports System.Runtime.InteropServices

		Dim gch = GCHandle.Alloc(This)
		TlsSetValue(tlsIndex, GCHandle.ToIntPtr(gch) As VoidPtr)

		With cs
			Dim hwnd = CreateWindowEx(.dwExStyle, .lpszClass, .lpszName, .style,
				.x, .y, .cx, .cy, .hwndParent, .hMenu, .hInstance, .lpCreateParams)
			createImpl = hwnd <> 0
		End With
	End Function

'--------------------------------
' ウィンドウプロシージャ
'Protected
Public
	Virtual Function WndProc(msg As DWord, wp As WPARAM, lp As LPARAM) As LRESULT
		Select Case msg
			Case WM_ERASEBKGND
				Dim rc = wnd.ClientRect
				Dim e = New PaintDCHandledEventArgs(wp As HDC, rc)
				OnPaintBackground(e)
				WndProc = e.Handled
			Case WM_PAINT
				Dim ps As PAINTSTRUCT
				wnd.BeginPaint(ps)
				Try
					Dim e = New PaintDCEventArgs(ps.hdc, ps.rcPaint)
					OnPaintDC(e)
				Finally
					wnd.EndPaint(ps)
				End Try
			Case WM_LBUTTONDOWN, WM_RBUTTONDOWN, WM_MBUTTONDOWN, WM_XBUTTONDOWN
				OnMouseDown(New MouseEventArgs(LOWORD(wp) As MouseButtons, 1, GET_X_LPARAM(lp), GET_Y_LPARAM(lp), 0))
			Case WM_LBUTTONUP, WM_RBUTTONUP, WM_MBUTTONUP, WM_XBUTTONUP
				OnMouseUp(New MouseEventArgs(LOWORD(wp) As MouseButtons, 1, GET_X_LPARAM(lp), GET_Y_LPARAM(lp), 0))
/*
			Case WM_KEYDOWN
				OnKeyDown(New KeyEventArgs(makeKeysFormWPLP(wp, lp)))
			Case WM_KEYUP
				OnKeyUp(New KeyEventArgs(makeKeysFormWPLP(wp, lp)))
			Case WM_CHAR
				OnKeyPress(New KeyPressEventArgs(wParam As Char))
			Case WM_ENABLE
				OnEnableChanged(EventArgs.Empty)
			Case WM_MOVE
				OnMove(EventArgs.Empty)
			Case WM_SIZE
				OnResize(EventArgs.Empty)
*/
			Case Else
			WndProc = DefWndProc(msg, wp, lp)
		End Select
	End Function

	Virtual Function DefWndProc(msg As DWord, wp As WPARAM, lp As LPARAM) As LRESULT
		DefWndProc = DefWindowProc(wnd.HWnd, msg, wp, lp)
	End Function

Private
	Function makeKeysFormWPLP(wp As WPARAM, lp As LPARAM) As Keys
		Dim t As DWord
		t = wp And Keys.KeyCode
		t Or= (GetKeyState(VK_SHIFT) As Word And &h8000) << 1
		t Or= (GetKeyState(VK_CONTROL) As Word And &h8000) << 2
		t Or= (GetKeyState(VK_MENU) As Word And &h8000) << 3
		makeKeysFormWPLP = t As Keys
	End Function


'--------------------------------
' ウィンドウメッセージ処理

'--------
' 2

Protected
	/*!
	@biref	ウィンドウの背景を描画する。
	@date	2007/12/04
	*/
	Virtual Sub OnPaintBackground(e As PaintDCBackGroundEventArgs)
		Dim hbr = (COLOR_3DFACE + 1) As HBRUSH
		FillRect(e.Handle, e.ClipRect, hbr)
		e.Handled = True
	End Sub

'--------
'イベント
' 3

#include "ControlEvent.sbp"

'--------------------------------
' 1 インスタンスメンバ変数
Private
	wnd As WindowHandle

'--------------------------------
' 1 初期ウィンドウクラス
Private
	Static Function WndProcFirst(hwnd As HWND, msg As DWord, wp As WPARAM, lp As LPARAM) As LRESULT
		Imports System.Runtime.InteropServices

		Dim rThis = Control.FromHWndCore(hwnd)
		If IsNothing(rThis) Then
			Dim gchValue = TlsGetValue(tlsIndex) As LONG_PTR
			TlsSetValue(tlsIndex, 0)
			If gchValue = 0 Then
				Goto *InstanceIsNotFound
			End If
			Dim gch = GCHandle.FromIntPtr(gchValue)
			rThis = gch.Target As Control
			' ウィンドウが作られて最初にWndProcFirstが呼ばれたとき

			If IsNothing(rThis) Then
				Goto *InstanceIsNotFound
			End If
			rThis.wnd = New WindowHandle(hwnd)
			rThis.wnd.SetProp(PropertyInstance, gchValue As HANDLE)
		End If
		WndProcFirst = rThis.WndProc(msg, wp, lp)
		If msg = WM_NCDESTROY Then
			Dim gchValue = GetProp(hwnd, PropertyInstance As ULONG_PTR As PCTSTR) As ULONG_PTR
			If gchValue <> 0 Then
				Dim gch = GCHandle.FromIntPtr(gchValue)
				gch.Free()
			End If
		End If

		Exit Function

	*InstanceIsNotFound
		OutputDebugString("ActiveBasic.Windows.UI.Control.WndProcFirst: The attached instance is not found.")
		WndProcFirst = DefWindowProc(hwnd, msg, wp, lp)
	End Function
	
'	Static Const WM_CONTROL_INVOKE = WM_USER + 0 As DWord
'	Static Const WM_CONTROL_BEGININVOKE = WM_USER + 1 As DWord

'--------------------------------
' 1 初期化終了関連（特にウィンドウクラス）

	'ウィンドウ作成時にウィンドウプロシージャへThisを伝えるためのもの
	Static tlsIndex As DWord

	Static hInstance As HINSTANCE
	Static atom As ATOM

	Static Const WindowClassName = "ActiveBasic.Windows.UI.Control"
	Static Const PropertyInstance = 0 As ATOM
Public
	Static Sub Initialize(hinst As HINSTANCE)
		tlsIndex = TlsAlloc()
		hInstance = hinst

		Dim PropertyInstanceString = WindowClassName + " " + Hex$(GetCurrentProcessId())
		PropertyInstance = GlobalAddAtom(ToTCStr(PropertyInstanceString))

		Dim wcx As WNDCLASSEX
		With wcx
			.cbSize = Len (wcx)
			.style = CS_HREDRAW Or CS_VREDRAW Or CS_DBLCLKS
			.lpfnWndProc = AddressOf (WndProcFirst)
			.cbClsExtra = 0
			.cbWndExtra = 0
			.hInstance = hinst
			.hIcon = 0
			.hCursor = LoadImage(0, MAKEINTRESOURCE(IDC_ARROW), IMAGE_CURSOR, 0, 0, LR_DEFAULTSIZE Or LR_SHARED) As HCURSOR
			.hbrBackground = 0
			.lpszMenuName = 0
			.lpszClassName = ToTCStr(WindowClassName)
			.hIconSm = 0
		End With
		atom = RegisterClassEx(wcx)
		If atom = 0 Then
			Dim buf[1023] As TCHAR
			wsprintf(buf, Ex"ActiveBasic.Windows.UI.Control.Control: RegisterClasseEx failed. Error code: &h%08X\r\n", GetLastError())
			OutputDebugString(buf)
			Debug
			ExitThread(0)
		End If
	End Sub

	Static Sub Uninitialize()
		UnregisterClass(atom As ULONG_PTR As PCSTR, hInstance)
		TlsFree(tlsIndex)
		GlobalDeleteAtom(PropertyInstance)
	End Sub
End Class

Namespace Detail
Class _System_ControlIinitializer
Public
	Sub _System_ControlIinitializer(hinst As HINSTANCE)
		Control.Initialize(hinst)
	End Sub

	Sub ~_System_ControlIinitializer()
		Control.Uninitialize()
	End Sub
End Class

#ifndef _SYSTEM_NO_INITIALIZE_CONTROL_
Dim _System_ControlInitializer As _System_ControlIinitializer(GetModuleHandle(0))
#endif '_SYSTEM_NO_INITIALIZE_CONTROL_

End Namespace 'Detail

Class Form
	Inherits Control
Protected
	Override Sub GetCreateStruct(ByRef cs As CREATESTRUCT)
		With cs
			.lpCreateParams = 0
			'.hInstance
			.hMenu = 0
			.hwndParent = 0
			.cy = CW_USEDEFAULT
			.cx = CW_USEDEFAULT
			.y = CW_USEDEFAULT
			.x = CW_USEDEFAULT
			.style = WS_OVERLAPPEDWINDOW
			.lpszName = ""
			'.lpszClass
			.dwExStyle = 0
		End With
	End Sub
Public '仮
	Override Function WndProc(msg As DWord, wp As WPARAM, lp As LPARAM) As LRESULT
		WndProc = 0
		Select Case msg
			Case WM_DESTROY
				PostQuitMessage(0)
			Case Else
				WndProc = Super.WndProc(msg, wp, lp)
		End Select
	End Function
End Class

End Namespace 'Forms
End Namespace 'UI
End Namespace 'Widnows
End Namespace 'ActiveBasic

'----------
'テスト実行用

Imports ActiveBasic.Windows.UI.Forms

Class Bar
Public
Static Sub PaintDCEvent(sender As Object, et As PaintDCEventArgs) 
	Dim e = et As PaintDCEventArgs
	TextOut(e.Handle, 10, 10, "Hello, world", 12)
End Sub
End Class

Dim f = New Form
f.Create()
Dim v = New PaintDCEventHandler(AddressOf (Bar.PaintDCEvent))
f.AddPaintDC(v)
f.Handle.Show(SW_SHOW)

MessageBox(0, "hello", "", 0)
