[MFC]키보드로부터 입력
'Page Up'이나 'Page Down'등을 눌렀을 때 응용 프로그램이 반응하려면 WM_KEYDOWN이나 WM_KEYUP 메시지를 처리한다.
인쇄 가능한 문자를 나타내는 키를 눌렀을 때는 (키누름이나 키해제 메시지를 무시하고) 키보드에서 입력된 문자를 나타내는 WM_CHAR 메시지를 처리하면 된다.
WM_KEYUP/DOWN 메시지 대신 WM_CHAR메시지를 사용하면 이벤트와 키 누름 주위의 상황(Shift가 눌러져 있는지 Caps Lock이 켜져 있는지)등에 대한 요인들을 Windows가 처리해주므로 쉽게 문자를 처리할 수 있다.
입력포커스
키보드로 부터 입력을 받는 창은 포커스를 갖고 있는 창이다.
Windows는 WM_SETFOCUS와 WM_KILLFOCUS 메시지를 통해 입력 포커스를 받거나 잃은 것에 대한 정보를 창에 알린다.
void CWnd::OnSetFocus(CWnd* pOldWnd) // pOldWnd는 포커스를 잃은 창
void CWnd::OnKillFocus(CWnd* pNewWnd) // pNewWnd는 포커스를 얻은 창
CWnd::SetFocus() 를 사용하면 입력 포커스를 다른 창으로 옮길 수 있다.
CWnd* pFocusWnd = CWnd::GetFocus() 를 사용하면 현재 누가 입력포커스를 갖고 있는지 알 수 있다.(다른 응용프로그램의 창 포인터는 얻을 수 없다. NULL이 리턴된다.)
키입력 메시지
키가 눌려지면 입력포커스가 있는 창에 가상 키코드와 함께 WM_KEYDOWN 메시지가 전달된다.
그리고, 키가 해제되면 WM_KEYUP 메시지가 전달된다.
'Alt'와 'F10'키를 제외한 모든 키는 WM_KEYDOWN/WM_KEYUP 메시지를 발생시킨다.
Alt 키를 누르면 WM_KEYDOWN/WM_KEYUP이 아니라 WM_SYSKEYDOWN/WM_SYSKEYUP 메시지가 발생한다.
F10 키를 누르면 메뉴 바로가기 처리 상태로 변환된다.
WM_KEYDOWN/WM_KEYUP, WM_SYSKEYDOWN/WM_SYSKEYUP은 각각 OnKeyDown/OnKeyUp, OnSysKeyDown/ONKeyUp 메시지 핸들러 함수와 연결된다.
위의 키입력 메시지 핸들러 함수의 원형은 다음과 같다.
afx_msg void OnMsgName( UINT nChar, UINT nRepCnt, UINT nFlags )
nChar : 누르거나 뗀 키의 가상 키 코드
nRepCnt : 키 누름 반복 횟수, 한 키를 계속 누르고 있는 경우, 한 메시지로 처리하기 위해..
nFlags : 키의 스캔코드와 아래표에 정리한 비트 플래그의 조합
0~7 : OEM 스켄코드 - 8비트 OEM 스캔코드
8 : 확장 키 플래그 - 확장키는 1, 아니면 0
9~10 : 예약됨 - N/A
13 : 컨텍스트 코드 - Alt 키가 눌러져 있으면 1, 아니면 0
14 : 이전 키의 상태 - 바로전에 키를 눌렀으면 1, 아니면 0
15 : 변화 상태 - 키가 눌려 있으면 0, 아니면 1
** 컨텍스트코드는 메시지가 생성될 때 Alt 키가 눌려졌는지를 나타낸다.
일반적으로 WM_SYSKEYDOWN/WM_SYSKEYUP은 Windows가 처리하도록 한다.(::DefWindowProc)
가상 키 코드
Windows는 가상키코드를 통해 키를 식별하므로 프로그래머는 키보드마다 달라지는 하드코드값이나 OEM스캔코드값을 신경쓸 필요가 없다.
가상 키 코드는 Winuser.h에 정의되어 있다.
문자처리는 위의 키입력 메시지 핸들러에서 가상 키를 처리하는 것보다 WM_CHAR 메시지로 처리하는게 더 좋다.
** 가상키를 제공하지 않는.. 문자도 숫자도 아닌 키도 있고.. 이러한 키는 국가별로 다르기도 하고.. 등등..
Shift 키 상태와 토글
WM_KEYDOWN/WM_KEYUP, WM_SYSKEYDOWN/WM_SYSKEYUP 메시지를 처리할 때 Shift, Ctrl, Alt 의 상태를 확인해야 할 필요가 있을 것이다.
이때는 ::GetKeyState 함수를 이용한다. 이 함수는 원하는 키가 눌려져 있는지 보고한다.
::GetKeyState(VK_SHIFT) // Shift 키가 눌려져 있으면 음수값을 리턴.
::GetKeyState(VK_CONTROL) // Ctrl 키가 눌려져 있으면 음수값을 리턴.
..
// Ctrl + 왼쪽 화살표 일때만 ...
if ((nChar == VK_LEFT) && (::GetKeyState(VK_CONTROL) < 0)) {
...
}
** GetKeyState에서 Alt를 체크할 필요는 없을 것이다. Alt 키는 누르면 WM_KEYDOWN/UP 이 아니라 WM_SYSKEYDOWN/UP 메시지가 발생하니 해당 핸들러함수에서 처리하면 된다.
** VK_LBUTTON, VK_MBUTTON, VK_RBUTTON 식별자와 GetKeyState 함수를 결합하면 마우스 단추가 눌려 잇는지 알 수 있다.
** GetKeyState는 다음과 같이 Num Lock, Caps Lock, Scroll Lock 가 켜져 있는지 꺼져 있는지 확인할 수 있다.
::GetKeyState( VK_NUMLOCK ) & 0x01 // 켜져 있으면 0
::GetKeyState 는 키보드 메시지 핸들러에서만 사용해야 한다.
키보드 메시지 핸들러 외부에서 키나 마우스단추를 확인하려면 ::GetAsyncKeyState 를 사용한다.
문자 메시지
WM_KEYDOWN/UP등과 같은 키보드 메시지 핸들러 텍스트 편집기와 같은 응용 프로그램을 만든다면 어떻게 작업해야 할까...
프로그래머는 직접 가상 키코드값과 Shift, Caps Lock, Ctrl 등의 키 조합을 다 다루어야 할 것이다.
그러나 Windows는 ::TranslateMessage API 함수를 통해서 문자키를 갖는 키 입력 메시지를 WM_CHAR 메시지로 변환해준다.
따라서 문자가 들어가는 키입력은 WM_CHAR 메시지 핸들러에서 처리하면 된다.
** Alt가 눌려진 상태에서 문자가 들어간 키입력은 WM_SYSCHAR 메시지 핸들러로 처리한다.
** Alt 조합은 일반적으로 특별한 목적에 사용되기 때문에 대부분 WM_SYSCHAR는 무시한다.
afx_msg void OnChar( UINT nChar, UINT nRepCnt, UINT nFlags )
nChar : ANSI나 Unicode 문자 코드가 저장된다.
nRepCnt, nFlags 는 키 입력 메시지와 동일하다.
데드 키 메시지
언급되지 않은 키보드 메시지가 2개 있다. 이 메시지는 거의 사용되지 않는다.
데드 키는 발음 부호를 나타낸다. 즉, 데드 키를 입력한 다음 문자를 입력하면 액센트 기호가 붙은 문자를 표현할 수 있다.
::TranslateMessage 는 데드키에 해당하는 WM_KEYUP를 WM_DEARCHAR로 변환하고 데드 키에 해당하는 WM_SYSKEYUP을 WM_SYSDEADCHAR 메시지로 변환한다.
캐럿
CWnd는 다음과 같이 7개의 캐럿 함수를 제공한다. 그리고 제공되지는 않지만 ::DestroyCaret 함수도 반드시 필요한 함수이다.
CreateCaret - 비트맵을 이용하여 캐럿을 만든다.
CreateSolidCaret - 실선 캐럿이나 블록 캐럿을 만든다.
CreateGrayCaret - 회색 선 캐럿이나 블록 캐럿을 만든다.
GetCaretPos - 캐럿의 위치를 얻는다.
SetCaretPos - 캐럿의 위치를 설정한다.
ShowCaret - 캐럿을 표시한다.
HideCaret - 캐럿을 감춘다.
** ::DestroyCaret 생성된 캐럿을 파괴한다.
캐럿은 동일한 스레드에서 실행되는 모든 창에 의해 공유되는 스레드 단위 자원이다.
캐럿을 사용하는 규칙은 다음과 같다.
1. 입력 포커스를 받을 때 캐럿을 만들어야 하고 입력 포커스를 잃을 때는 삭제해야 한다.
2. 캐럿이 만들어져 있다해도 ShowCaret를 해야 표시된다. HideCaret를 하면 숨길 수 있다.
HIdeCaret를 두번연속 했다면 ShowCaret도 두번해야 캐럿을 다시 볼 수 있다.
3. OnPaint 외부에서 캐럿을 포함하고 있는 창 영역을 그릴 때는 캐럿을 숨겨야 한다.
(안그러면 화면이 깨진다.)
OnPaint 안에서 캐럿을 숨기거나 다시 표시할 필요는 없다.
::BeginPain와 ::EndPaint가 알아서 해준다.
4. SetFocusPos로 캐럿을 이동시킬 수 있다.
즉, 윈도우가 자동으로 이동시켜주지 않는다. 프로그래머가 알아서 해야 한다.
캐럿의 폭을 찾 테두리의 폭에 맞추는 경우도 있다.
SM_CXBORDER 값과 함께 ::GetSystemMetrics를 호출하면 창 테두리의 폭을 알 수 있다.
고정폭 글꼴에서는 캐럿의 폭과 높이를 글자 하나의 크기와 같도록 할 수도 있다.
캐럿이동은 CWnd::SetCaretPos로 한다.
고정폭 글꼴은 문자의 위치에 한 문자의 폭을 곱하면 새로운 문자의 위치를 알 수 있다.
가변폭 글꼴은 CDC::GetTextExtent나 CDC::GetTabbedTextExtent 함수를 이용하여 논리적 단위로 가변폭 글꼴의 문자열 폭을 계산할 수 있다.
** CWnd나 CWnd를 상속받은 창에서 캐럿을 사용하려면 대단히 귀찮은 작업들을 많이 해줘야 한다. 따라서 웬만하면 CEdit와 같은 컨트롤로 해결하도록 한다.
그러나... CEdit와 같은 클래스로 할 수 없는 다양한 에디팅처리가 꼭 필요하다면 할수 없이 캐럿을 사용해야 한다.
첨부된 파일은 CWnd를 상속받은 창에서 캐럿을 처리하는 정석을 보여준다.
'WindowsPrograming' 카테고리의 다른 글
WM_KEYDOWN 메시지 (0) | 2007.04.09 |
---|---|
DC (Device Context) (0) | 2007.04.05 |
SetWindowEXt() SetViewportExt()는 (2) | 2007.04.04 |