윈도우 프로그래밍 Visual C++ 2010 MFC Programming(김선우, 신화서 저) 5장 심화문제입니다.
2번 3번은 https://blog.naver.com/mark1007/220988574718 참고했습니다.
[5-1]
// ChildView.h: CChildView 클래스의 인터페이스
//
#pragma once
// CChildView 창
class CChildView : public CWnd
{
// 생성입니다.
public:
CChildView();
// 특성입니다.
public:
BOOL m_bDrawMode; //그리기 작업이 진행중임을 나타낸다
int m_x1, m_y1, m_x2, m_y2; //타원에 외접하는 직사각형의 좌상단/우하단 좌표
// 작업입니다.
public:
// 재정의입니다.
protected:
virtual BOOL PreCreateWindow(CREATESTRUCT& cs);
// 구현입니다.
public:
virtual ~CChildView();
// 생성된 메시지 맵 함수
protected:
afx_msg void OnPaint();
DECLARE_MESSAGE_MAP()
public:
afx_msg void OnMouseMove(UINT nFlags, CPoint point);
afx_msg void OnLButtonDown(UINT nFlags, CPoint point);
afx_msg void OnLButtonUp(UINT nFlags, CPoint point);
afx_msg void OnKeyDown(UINT nChar, UINT nRepCnt, UINT nFlags);
};
// ChildView.cpp: CChildView 클래스의 구현
//ChangeCursor 예제를 다음과 같이 수정하시오
//마우스 커서가 타원 위에 올라갔을 때 마우스 왼쪽 버튼을 누르고 드래그하면 타원이 이동하고
//버튼을 놓으면 멈춘다.
//그리고 마우스 왼쪽 버튼을 누른 상태에서 방향키를 사용해 마우스를 1픽셀씩 움직인다
#include "stdafx.h"
#include "심화문제 5-1(2).h"
#include "ChildView.h"
#ifdef _DEBUG
#define new DEBUG_NEW
#endif
// CChildView
CChildView::CChildView()
{
m_bDrawMode = FALSE;
m_x1 = 10;
m_y1 = 10;
m_x2 = 400;
m_y2 = 100;
}
CChildView::~CChildView()
{
}
BEGIN_MESSAGE_MAP(CChildView, CWnd)
ON_WM_PAINT()
ON_WM_MOUSEMOVE()
ON_WM_LBUTTONDOWN()
ON_WM_LBUTTONUP()
ON_WM_KEYDOWN()
END_MESSAGE_MAP()
// CChildView 메시지 처리기
BOOL CChildView::PreCreateWindow(CREATESTRUCT& cs)
{
if (!CWnd::PreCreateWindow(cs))
return FALSE;
cs.dwExStyle |= WS_EX_CLIENTEDGE;
cs.style &= ~WS_BORDER;
cs.lpszClass = AfxRegisterWndClass(CS_HREDRAW|CS_VREDRAW|CS_DBLCLKS,
::LoadCursor(NULL, IDC_ARROW), reinterpret_cast<HBRUSH>(COLOR_WINDOW+1), NULL);
return TRUE;
}
void CChildView::OnPaint()
{
CPaintDC dc(this); // 그리기를 위한 디바이스 컨텍스트입니다.
// TODO: 여기에 메시지 처리기 코드를 추가합니다.
dc.SelectStockObject(LTGRAY_BRUSH);
dc.Ellipse(m_x1, m_y1, m_x2, m_y2);
// 그리기 메시지에 대해서는 CWnd::OnPaint()를 호출하지 마십시오.
}
void CChildView::OnMouseMove(UINT nFlags, CPoint point)
{
// TODO: 여기에 메시지 처리기 코드를 추가 및/또는 기본값을 호출합니다.
if (m_bDrawMode)
{
Invalidate();
CClientDC dc(this);
dc.SelectStockObject(NULL_BRUSH);
//이전에 그린 타원 지운다
dc.SetROP2(R2_NOT);
dc.Ellipse(m_x1, m_y1, m_x2, m_y2);
//새로운 타원 그린다
CPoint cursor;
::GetCursorPos(&cursor);
ScreenToClient(&cursor); //커서의 위치를 클라이언트 좌표로
dc.SetROP2(R2_NOT);
m_x1 = cursor.x - 195;
m_x2 = cursor.x + 195;
m_y1 = cursor.y - 45;
m_y2 = cursor.y + 45;
dc.Ellipse(m_x1, m_y1, m_x2, m_y2);
}
CWnd::OnMouseMove(nFlags, point);
}
void CChildView::OnLButtonDown(UINT nFlags, CPoint point)
{
// TODO: 여기에 메시지 처리기 코드를 추가 및/또는 기본값을 호출합니다.
//그리기 모드 시작
m_bDrawMode = TRUE;
//좌표 저장
m_x1 = point.x - 195;
m_x2 = point.x + 195;
m_y1 = point.y - 45;
m_y2 = point.y + 45;
CWnd::OnLButtonDown(nFlags, point);
}
void CChildView::OnLButtonUp(UINT nFlags, CPoint point)
{
// TODO: 여기에 메시지 처리기 코드를 추가 및/또는 기본값을 호출합니다.
CClientDC dc(this);
//그리기 모드 끝낸다
m_bDrawMode = FALSE;
Invalidate();
CWnd::OnLButtonUp(nFlags, point);
}
void CChildView::OnKeyDown(UINT nChar, UINT nRepCnt, UINT nFlags)
{
// TODO: 여기에 메시지 처리기 코드를 추가 및/또는 기본값을 호출합니다.
CPoint inside;
::GetCursorPos(&inside);
ScreenToClient(&inside); //커서의 위치를 클라이언트 좌표로
CRgn rgn;
rgn.CreateEllipticRgn(m_x1, m_y1, m_x2, m_y2);
if (m_bDrawMode && rgn.PtInRegion(inside))
{
CPoint cursor;
::GetCursorPos(&cursor);
switch (nChar)
{
case VK_LEFT:
cursor.x--;
break;
case VK_RIGHT:
cursor.x++;
break;
case VK_UP:
cursor.y--;
break;
case VK_DOWN:
cursor.y++;
break;
}
::SetCursorPos(cursor.x, cursor.y);
}
CWnd::OnKeyDown(nChar, nRepCnt, nFlags);
}
[5-2]
// ChildView.h: CChildView 클래스의 인터페이스
//
#pragma once
// CChildView 창
class CChildView : public CWnd
{
// 생성입니다.
public:
CChildView();
// 특성입니다.
public:
CString str;
CRect rect;
BOOL bCaret;
// 작업입니다.
public:
CPoint UpdateCaret(BOOL bCalc = FALSE);
// 재정의입니다.
protected:
virtual BOOL PreCreateWindow(CREATESTRUCT& cs);
// 구현입니다.
public:
virtual ~CChildView();
// 생성된 메시지 맵 함수
protected:
afx_msg void OnPaint();
DECLARE_MESSAGE_MAP()
public:
afx_msg void OnLButtonDown(UINT nFlags, CPoint point);
afx_msg void OnChar(UINT nChar, UINT nRepCnt, UINT nFlags);
afx_msg void OnKillFocus(CWnd* pNewWnd);
};
// ChildView.cpp: CChildView 클래스의 구현
//ChangeCursor 예제를 다음과 같이 수정하시오.
//클라이언트 영역에 타원 대신 사각ㅎ형을 하나 그린다.
//그리고 마우스로 해당 사각형 내부를 클릭하면 캐럿이 생기고 키보드로 글자를 입력할 수 있다.
#include "stdafx.h"
#include "심화문제 5-2.h"
#include "ChildView.h"
#ifdef _DEBUG
#define new DEBUG_NEW
#endif
// CChildView
CChildView::CChildView()
{
bCaret = FALSE;
rect = CRect(10, 10, 100, 100);
}
CChildView::~CChildView()
{
}
BEGIN_MESSAGE_MAP(CChildView, CWnd)
ON_WM_PAINT()
ON_WM_LBUTTONDOWN()
ON_WM_CHAR()
ON_WM_KILLFOCUS()
END_MESSAGE_MAP()
// CChildView 메시지 처리기
BOOL CChildView::PreCreateWindow(CREATESTRUCT& cs)
{
if (!CWnd::PreCreateWindow(cs))
return FALSE;
cs.dwExStyle |= WS_EX_CLIENTEDGE;
cs.style &= ~WS_BORDER;
cs.lpszClass = AfxRegisterWndClass(CS_HREDRAW|CS_VREDRAW|CS_DBLCLKS,
::LoadCursor(NULL, IDC_ARROW), reinterpret_cast<HBRUSH>(COLOR_WINDOW+1), NULL);
return TRUE;
}
void CChildView::OnPaint()
{
CPaintDC dc(this); // 그리기를 위한 디바이스 컨텍스트입니다.
// TODO: 여기에 메시지 처리기 코드를 추가합니다.
CRect tempRect = rect;
tempRect.InflateRect(10, 0);
dc.Rectangle(tempRect);
dc.DrawText(str, &rect, DT_LEFT | DT_VCENTER | DT_SINGLELINE);
// 그리기 메시지에 대해서는 CWnd::OnPaint()를 호출하지 마십시오.
}
//마우스 왼쪽 버튼을 클릭했을 때, 그 위치가 사각형 영역안이면 캐럿 표시
void CChildView::OnLButtonDown(UINT nFlags, CPoint point)
{
// TODO: 여기에 메시지 처리기 코드를 추가 및/또는 기본값을 호출합니다.
if (rect.PtInRect(point) == TRUE)
{
TEXTMETRIC tm;
CClientDC dc(this);
dc.GetTextMetrics(&tm);
CreateSolidCaret(2, tm.tmHeight); //캐럿 생성
ShowCaret(); //화면에 보이도록 한다
UpdateCaret();
bCaret = TRUE;
}
else
{
HideCaret();
::DestroyCaret();
bCaret = FALSE;
}
CWnd::OnLButtonDown(nFlags, point);
}
//캐럿의 위치를변경하는 함수
//캐럿의 위치를 결정하기 위해 출력할 공간을 먼저 계산한 ㅎ후 이동
//CDC 클래스의 GetTextMetrics() 함수는 현재 폰트의 정보를 TEXTMETRIC 구조체 형태로 알려줌
CPoint CChildView::UpdateCaret(BOOL bCalc)
{
TEXTMETRIC tm;
CClientDC dc(this);
dc.GetTextMetrics(&tm);
CRect tempRect = rect;
CPoint coord = tempRect.CenterPoint();
if (str.IsEmpty() == FALSE)
{
dc.DrawText(str, tempRect, DT_CALCRECT | DT_LEFT | DT_VCENTER | DT_SINGLELINE);
coord.x = tempRect.right;
}
else
coord.x = tempRect.left;
coord.y -= tm.tmHeight / 2;
if (bCalc == FALSE)
SetCaretPos(coord); //위치설정
return coord;
}
//키보드입력을 받는다
//UpdateCaret() 함수를 사용ㅎ하여 영역을 벗어나는지 검사
void CChildView::OnChar(UINT nChar, UINT nRepCnt, UINT nFlags)
{
// TODO: 여기에 메시지 처리기 코드를 추가 및/또는 기본값을 호출합니다.
if (nChar >= 32 && bCaret == TRUE)
{
CString strBackup;
strBackup = str;
str += (char)nChar;
//영역 벗어나는지 검사
CPoint inside = UpdateCaret(TRUE);
if (rect.PtInRect(inside) == FALSE)
str = strBackup;
UpdateCaret();
Invalidate();
}
CWnd::OnChar(nChar, nRepCnt, nFlags);
}
void CChildView::OnKillFocus(CWnd* pNewWnd)
{
CWnd::OnKillFocus(pNewWnd);
HideCaret();
::DestroyCaret();
// TODO: 여기에 메시지 처리기 코드를 추가합니다.
}
[5-3]
// ChildView.h: CChildView 클래스의 인터페이스
//
#pragma once
// CChildView 창
class CChildView : public CWnd
{
// 생성입니다.
public:
CChildView();
// 특성입니다.
public:
CString m_str;
CPoint m_ptCaret;
// 작업입니다.
public:
CPoint UpdateCaret(int index = -1, BOOL bCalc = FALSE);
CString FindRowString(CString &str, DWORD dwRow, int *indexFound = NULL);
// 재정의입니다.
protected:
CPoint GetCaretPoint(int index = -1);
int GetCharIndex(CPoint point);
int GetVertCharIndex(CPoint point, BOOL bUpward);
virtual BOOL PreCreateWindow(CREATESTRUCT& cs);
// 구현입니다.
public:
virtual ~CChildView();
// 생성된 메시지 맵 함수
protected:
afx_msg void OnPaint();
DECLARE_MESSAGE_MAP()
public:
afx_msg void OnSetFocus(CWnd* pOldWnd);
afx_msg void OnChar(UINT nChar, UINT nRepCnt, UINT nFlags);
afx_msg void OnKeyDown(UINT nChar, UINT nRepCnt, UINT nFlags);
afx_msg void OnKillFocus(CWnd* pNewWnd);
};
// ChildView.cpp: CChildView 클래스의 구현
//캐럿을 표시하고 방향키를 사용하여 입력한 문자 내에서
//이동할 수 있도록 inputCharacter 예제를 수정하시오
#include "stdafx.h"
#include "심화문제 5-3.h"
#include "ChildView.h"
#ifdef _DEBUG
#define new DEBUG_NEW
#endif
// CChildView
CChildView::CChildView()
{
m_ptCaret = CPoint(0, 0);
}
CChildView::~CChildView()
{
}
BEGIN_MESSAGE_MAP(CChildView, CWnd)
ON_WM_PAINT()
ON_WM_SETFOCUS()
ON_WM_CHAR()
ON_WM_KEYDOWN()
ON_WM_KILLFOCUS()
END_MESSAGE_MAP()
// CChildView 메시지 처리기
BOOL CChildView::PreCreateWindow(CREATESTRUCT& cs)
{
if (!CWnd::PreCreateWindow(cs))
return FALSE;
cs.dwExStyle |= WS_EX_CLIENTEDGE;
cs.style &= ~WS_BORDER;
cs.lpszClass = AfxRegisterWndClass(CS_HREDRAW|CS_VREDRAW|CS_DBLCLKS,
::LoadCursor(NULL, IDC_ARROW), reinterpret_cast<HBRUSH>(COLOR_WINDOW+1), NULL);
return TRUE;
}
void CChildView::OnPaint()
{
CPaintDC dc(this); // 그리기를 위한 디바이스 컨텍스트입니다.
// TODO: 여기에 메시지 처리기 코드를 추가합니다.
CRect rect;
GetClientRect(&rect);
dc.DrawText(m_str, &rect, DT_LEFT | DT_TOP);
// 그리기 메시지에 대해서는 CWnd::OnPaint()를 호출하지 마십시오.
}
//OnSetFocus() 함수에서 포커스를 받을 때 캐럿을 화면에 보이도록 처리
void CChildView::OnSetFocus(CWnd* pOldWnd)
{
CWnd::OnSetFocus(pOldWnd);
// TODO: 여기에 메시지 처리기 코드를 추가합니다.
TEXTMETRIC tm;
CClientDC dc(this);
dc.GetTextMetrics(&tm);
CreateSolidCaret(2, tm.tmHeight); //캐럿 생성
ShowCaret(); //화면에 보이도록 한다
SetCaretPos(m_ptCaret);
}
//키보드 입력 문자 처리
//현재 문자를 입력하는 위치가 문장의 끝이면 m_str 멤버 변수에 현재 문자 추가
//문자 중간이면 덮어쓰기 모드로 처리하여 해당 문자를 새로운 문자로 교체
void CChildView::OnChar(UINT nChar, UINT nRepCnt, UINT nFlags)
{
// TODO: 여기에 메시지 처리기 코드를 추가 및/또는 기본값을 호출합니다.
if (nChar >= 32)
{
int idx = GetCharIndex(m_ptCaret);
if (idx == m_str.GetLength())
{
m_str += (char)nChar;
m_ptCaret = UpdateCaret();
}
else
{
//덮어쓰기
m_str.Format(_T("%s%c%s"), m_str.Left(idx), nChar, m_str.Mid(idx + 1));
m_ptCaret = UpdateCaret(idx + 1);
}
Invalidate();
}
CWnd::OnChar(nChar, nRepCnt, nFlags);
}
//GetCharIndex() 함수는 현재의 위치를 m_str 멤버 변수 문자열의 인덱스로 계산해 주는 함수
//먼저 폰트 높이를 이용해 몇 번째 줄인지 찾아내고 FindRowString() 멤버 함수로 해당 줄의 문자열을 알아낸다
//그 문자열을 한 줄로 출력해 보면서 현재 위치와 비교하여 인덱스 계산
int CChildView::GetCharIndex(CPoint point)
{
TEXTMETRIC tm;
CClientDC dc(this);
dc.GetTextMetrics(&tm);
CRect rect;
GetClientRect(rect);
DWORD dwRow = point.y / tm.tmHeight;
int idx = 0;
CString str = FindRowString(m_str, dwRow, &idx);
if (str.IsEmpty() == TRUE)
return idx;
if (point.x == 0)
return idx;
CString strFraction;
CPoint coord(point);
CRect rectCalc;
for (int i = 1; i <= str.GetLength(); i++)
{
rectCalc = rect;
strFraction = str.Left(i);
dc.DrawText(strFraction, rectCalc, DT_CALCRECT | DT_LEFT | DT_TOP | DT_SINGLELINE);
if (point.x == rectCalc.right)
return idx + i;
}
return -1;
}
CString CChildView::FindRowString(CString &str, DWORD dwRow, int *indexFound)
{
int idx = 0, idxNext = -1;
for (DWORD i = 0; i <= dwRow; i++)
{
idx = idxNext + 1;
idxNext = str.Find(VK_RETURN, idx);
}
if (idxNext == -1)
idxNext = str.GetLength();
if (indexFound != NULL)
*indexFound = idx;
return str.Mid(idx, idxNext - idx);
}
//GetVertCharIndex() 함수는 상하로 이동할 때 이동할 위치를 문자열 인덱스로 리턴하는 함수
//폰트가 고정폭인 경우에는 상하 이동이 크게 어렵지 않지만, 가변폭인 경우는 가장 가까운쪽으로 예측해야한다
//그 때문에 다음과 같이 행은 폰트 높이로 비교적 간단히 계산, 가로 위치는 현 위치, 앞, 뒤로 검색해야한다
//또한 해당 위치에 글자가 없으면 그 행의 가장 끝으로 이동하는 루틴도 필요하다
int CChildView::GetVertCharIndex(CPoint point, BOOL bUpward)
{
TEXTMETRIC tm;
CClientDC dc(this);
dc.GetTextMetrics(&tm);
CRect rect;
GetClientRect(rect);
CPoint coord(point);
if (bUpward == TRUE)
{
coord.y -= tm.tmHeight;
if (coord.y < 0)
return -1;
}
else
{
coord.y += tm.tmHeight;
CRect rectCalc(rect);
dc.DrawText(m_str, rectCalc, DT_CALCRECT | DT_LEFT | DT_TOP);
if (coord.y > rectCalc.bottom - tm.tmHeight)
return -1;
}
int dx = tm.tmAveCharWidth / 2;
for (int i = coord.x; i >= coord.x - dx; i--) //중간에서 앞쪽 검색
{
CPoint coordSearch(i, coord.y);
int idx = GetCharIndex(coordSearch);
if (idx != -1)
return idx;
}
for (int i = coord.x + 1; i <= coord.x + dx; i++) //뒤쪽 검색
{
CPoint coordSearch(i, coord.y);
int idx = GetCharIndex(coordSearch);
if (idx != -1)
return idx;
}
//행 끝으로 이동
DWORD dwRow = coord.y / tm.tmHeight;
int idx = 0;
CString str = FindRowString(m_str, bUpward ? dwRow++ : dwRow--, &idx);
return idx + str.GetLength();
}
//UpdateCaret() 함수는 인덱스로 캐럿의 위치를 계산한 후 캐럿의 위치를 변경하는 함수
//변경된 위치를 리턴하기 때문에 현재 위치를 갱신할 때 사용, 두번째 인자를 TRUE로 입력하면 계산 전용
CPoint CChildView::UpdateCaret(int idx, BOOL bCalc)
{
CPoint coord = GetCaretPoint(idx);
if (bCalc == FALSE)
SetCaretPos(coord); //위치 설정
return coord;
}
//GetCaretPoint() 함수는 이전의 GetCharIndex() 함수와 반대로, m_str 멤버 변수의 문자열 인덱스를 주면 화면의 좌표를 리턴하는 함수
//DrawText() 함수로 먼저 전체를 출력하여 현재 y 좌표 계산, FindRowString() 함수로 해당 줄의 문자열을 추출하여 그를 통해 x 좌표 계산
CPoint CChildView::GetCaretPoint(int idx)
{
TEXTMETRIC tm;
CClientDC dc(this);
dc.GetTextMetrics(&tm);
CRect rect;
GetClientRect(&rect);
CPoint coord(0, 0);
CString str = (idx == -1) ? m_str : m_str.Left(idx);
CRect rectCalc(rect);
if (str.IsEmpty() == FALSE)
{
dc.DrawText(str, rectCalc, DT_CALCRECT | DT_LEFT | DT_TOP);
coord.y = rectCalc.bottom - tm.tmHeight;
CString strRow = FindRowString(str, coord.y / tm.tmHeight);
if (strRow.IsEmpty() == FALSE)
{
rectCalc = rect;
dc.DrawText(strRow, rectCalc, DT_CALCRECT | DT_LEFT | DT_TOP | DT_SINGLELINE);
coord.x = rectCalc.right;
}
else
coord.x = 0;
}
return coord;
}
//화살표 키를 눌렀을 떄 캐럿 이동 처리
//좌우의 경우 GetCharIndex() 함수로 전달받은 인덱스를 감소 또는 증가시킨 후
//UpdateCaret() 함수로 적용시킨다
//상하의 경우 GetVertCharIndex() 함수를 사용하여 이동할 문자열 인덱스를 얻은 후 UpdateCaret() 함수를 적용
void CChildView::OnKeyDown(UINT nChar, UINT nRepCnt, UINT nFlags)
{
// TODO: 여기에 메시지 처리기 코드를 추가 및/또는 기본값을 호출합니다.
int idx = -1;
switch (nChar)
{
case VK_LEFT:
idx = GetCharIndex(m_ptCaret);
idx--;
if (idx < 0)
idx = 0;
m_ptCaret = UpdateCaret(idx);
break;
case VK_RIGHT:
idx = GetCharIndex(m_ptCaret);
idx++;
if (idx > m_str.GetLength())
idx = m_str.GetLength();
m_ptCaret = UpdateCaret(idx);
break;
case VK_UP:
idx = GetVertCharIndex(m_ptCaret, TRUE);
if (idx != -1)
m_ptCaret = UpdateCaret(idx);
break;
case VK_DOWN:
idx = GetVertCharIndex(m_ptCaret, FALSE);
if (idx != -1)
m_ptCaret = UpdateCaret(idx);
break;
}
if (nChar == VK_RETURN)
{
int idx = GetCharIndex(m_ptCaret);
if (idx == m_str.GetLength())
{
m_str += (char)nChar;
m_ptCaret = UpdateCaret();;
}
else
{
//삽입 모드
m_str.Format(_T("%s%c%s", m_str.Left(idx), nChar, m_str.Mid(idx)));
m_ptCaret = UpdateCaret(idx + 1);
}
Invalidate();
}
CWnd::OnKeyDown(nChar, nRepCnt, nFlags);
}
void CChildView::OnKillFocus(CWnd* pNewWnd)
{
CWnd::OnKillFocus(pNewWnd);
HideCaret(); //숨기고
::DestroyCaret(); //파괴한다
// TODO: 여기에 메시지 처리기 코드를 추가합니다.
}
[5-4]
// ChildView.h: CChildView 클래스의 인터페이스
//
#pragma once
// CChildView 창
class CChildView : public CWnd
{
// 생성입니다.
public:
CChildView();
// 특성입니다.
public:
BOOL m_bDrawMode; //그리기 모드
BOOL click, ctrl;
int m_x1, m_y1, m_x2, m_y2;
CList<CRect, CRect&> list;
// 작업입니다.
public:
// 재정의입니다.
protected:
virtual BOOL PreCreateWindow(CREATESTRUCT& cs);
// 구현입니다.
public:
virtual ~CChildView();
// 생성된 메시지 맵 함수
protected:
afx_msg void OnPaint();
DECLARE_MESSAGE_MAP()
public:
afx_msg void OnLButtonDown(UINT nFlags, CPoint point);
afx_msg void OnLButtonUp(UINT nFlags, CPoint point);
afx_msg void OnMouseMove(UINT nFlags, CPoint point);
afx_msg void OnKeyDown(UINT nChar, UINT nRepCnt, UINT nFlags);
afx_msg void OnKeyUp(UINT nChar, UINT nRepCnt, UINT nFlags);
};
// ChildView.cpp: CChildView 클래스의 구현
//ChangeCursor 예제를 다음과 같이 수정하시오
//마우스가 타원 위에 올라갔을 때 마우스 왼쪽 버튼을 누르고 드래긓하면 점선으로 된 타원이 그려지고
//버튼을 놓으면 이동을 끝낸다.
//그리고 ctrl 키를 누른 상태면 복사를 수행하고, 그렇지 않은 상태에서는 이동을 수행한다
#include "stdafx.h"
#include "심화문제 5-4.h"
#include "ChildView.h"
#ifdef _DEBUG
#define new DEBUG_NEW
#endif
// CChildView
CChildView::CChildView()
{
m_bDrawMode = FALSE;
ctrl = click = FALSE;
m_x1 = m_y1 = 10;
m_x2 = 400;
m_y2 = 100;
list.AddTail(CRect(m_x1, m_y1, m_x2, m_y2));
}
CChildView::~CChildView()
{
list.RemoveAll();
}
BEGIN_MESSAGE_MAP(CChildView, CWnd)
ON_WM_PAINT()
ON_WM_LBUTTONDOWN()
ON_WM_LBUTTONUP()
ON_WM_MOUSEMOVE()
ON_WM_KEYDOWN()
ON_WM_KEYUP()
END_MESSAGE_MAP()
// CChildView 메시지 처리기
BOOL CChildView::PreCreateWindow(CREATESTRUCT& cs)
{
if (!CWnd::PreCreateWindow(cs))
return FALSE;
cs.dwExStyle |= WS_EX_CLIENTEDGE;
cs.style &= ~WS_BORDER;
cs.lpszClass = AfxRegisterWndClass(CS_HREDRAW|CS_VREDRAW|CS_DBLCLKS,
::LoadCursor(NULL, IDC_ARROW), reinterpret_cast<HBRUSH>(COLOR_WINDOW+1), NULL);
return TRUE;
}
void CChildView::OnPaint()
{
CPaintDC dc(this); // 그리기를 위한 디바이스 컨텍스트입니다.
// TODO: 여기에 메시지 처리기 코드를 추가합니다.
dc.SelectStockObject(LTGRAY_BRUSH);
POSITION pos = list.GetHeadPosition();
while (pos != NULL)
{
CRect rect = list.GetNext(pos);
dc.Ellipse(rect);
}
// 그리기 메시지에 대해서는 CWnd::OnPaint()를 호출하지 마십시오.
}
void CChildView::OnLButtonDown(UINT nFlags, CPoint point)
{
// TODO: 여기에 메시지 처리기 코드를 추가 및/또는 기본값을 호출합니다.
m_bDrawMode = TRUE;
//좌표 저장
m_x1 = point.x - 195;
m_x2 = point.x + 195;
m_y1 = point.y - 45;
m_y2 = point.y + 45;
CWnd::OnLButtonDown(nFlags, point);
}
void CChildView::OnLButtonUp(UINT nFlags, CPoint point)
{
// TODO: 여기에 메시지 처리기 코드를 추가 및/또는 기본값을 호출합니다.
if (ctrl)
list.AddTail(CRect(m_x1, m_y1, m_x2, m_y2));
else
{
list.RemoveAll();
list.AddTail(CRect(m_x1, m_y1, m_x2, m_y2));
}
m_bDrawMode = FALSE;
Invalidate();
CWnd::OnLButtonUp(nFlags, point);
}
void CChildView::OnMouseMove(UINT nFlags, CPoint point)
{
// TODO: 여기에 메시지 처리기 코드를 추가 및/또는 기본값을 호출합니다.
if (m_bDrawMode && click == FALSE)
{
CClientDC dc(this);
CPen pen(PS_DASH, 1, RGB(0, 0, 0));
dc.SelectObject(&pen);
dc.SelectStockObject(NULL_BRUSH);
//이전에 그린 타원 지운다
dc.SetROP2(R2_NOT);
dc.Ellipse(m_x1, m_y1, m_x2, m_y2);
//새로운 타원 그린다
CPoint cursor;
::GetCursorPos(&cursor);
ScreenToClient(&cursor); //커서의 위치를 클라이언트 좌표로
dc.SetROP2(R2_NOT);
m_x1 = cursor.x - 195;
m_x2 = cursor.x + 195;
m_y1 = cursor.y - 45;
m_y2 = cursor.y + 45;
dc.Ellipse(m_x1, m_y1, m_x2, m_y2);
}
CWnd::OnMouseMove(nFlags, point);
}
void CChildView::OnKeyDown(UINT nChar, UINT nRepCnt, UINT nFlags)
{
// TODO: 여기에 메시지 처리기 코드를 추가 및/또는 기본값을 호출합니다.
if (nChar == VK_CONTROL)
ctrl = TRUE;
CWnd::OnKeyDown(nChar, nRepCnt, nFlags);
}
void CChildView::OnKeyUp(UINT nChar, UINT nRepCnt, UINT nFlags)
{
// TODO: 여기에 메시지 처리기 코드를 추가 및/또는 기본값을 호출합니다.
if (nChar == VK_CONTROL)
ctrl = FALSE;
CWnd::OnKeyUp(nChar, nRepCnt, nFlags);
}
개발환경:Visual Studio 2017
지적, 조언, 질문 환영입니다! 댓글 남겨주세요~
'MFC > 윈도우 프로그래밍' 카테고리의 다른 글
MFC 윈도우 프로그래밍 6장 연습문제(9~16) (0) | 2018.04.07 |
---|---|
MFC 윈도우 프로그래밍 6장 연습문제(1~8) (0) | 2018.04.06 |
MFC 윈도우 프로그래밍 5장 연습문제(9~16) (0) | 2018.03.30 |
MFC 윈도우 프로그래밍 5장 연습문제(1~8) (0) | 2018.03.29 |
MFC 윈도우 프로그래밍 4장 심화문제 (0) | 2018.03.26 |