C++/MFC

[MFC] 메모장(에디터 프로그램)

balabala 2021. 8. 24. 16:17
728x90
SMALL
- 설명 - 
프로젝트 명 : Notepad(단일 문서)
① 문자와 커서(캐럿)를 직접 view에 그려주는 방식
② Document-View 구조
③ Document의 Serialize()를 이용해 입력 내용을 저장 또는 열기

 

1. NotepadDoc.h
사용자 지정 변수
CString strDB[256] : 입력하는 문자열을 저장할 배열(256줄) -> 256줄 표시 가능
long pline : 현재 줄 표시

그림1. NotePadDoc.h 코드

 

2. NotepadDoc.cpp
변수 초기화
Serialize() : 내용 저장 및 열기. 줄의 개수와 문자열을 파일로 저장.

그림2. NotepadDoc.cpp 변수 초기화 코드

  • Serialize() 소스 보기
더보기
// CNotepadDoc serialization
// 줄의 개수와 문자열을 파일로 저장
void CNotepadDoc::Serialize(CArchive& ar)
{
    if (ar.IsStoring())
    {
        // TODO: 여기에 저장 코드를 추가합니다.
        // 줄과 스트링을 파일로 저장
        ar << pline;
        for (int i = 0; i <= pline; i++) {
            ar << strDB[i];
        }
    }
    else
    {
        // TODO: 여기에 로딩 코드를 추가합니다.
        ar >> pline;
        for (int i = 0; i <= pline; i++) {
            ar >> strDB[i];
        }
    }
}
 
3. NotepadView.h
사용자 지정 변수
CPoint pntCur : 현재 캐럿의 위치 저장
CNotepadDoc *pDoc : Document 포인터
int lastline : 마지막 라인 기억

그림3. NotepadView.h 코드

4. NotepadView.cpp
① 변수 초기화 코드 추가
② WM_CREATE 메시지 함수(OnCreate) : 최초 캐럿 위치 및 캐럿의 크기 지정.
③ WM_CHAR 메세지 함수(OnChar) : 문자 출력.
④ WM_KEYUP 메세지 함수(OnKeyUp) : WM_CHAR 메시지는 출력이 불가능한 키(기능키, SHIFT, CTRL, ALT, 방향키 등)를 처리할수 없으므로, 방향키를 처리하기 위해 WM_KEYUP 메세지 함수 추가.
⑤ WM_PAINT 메세지 함수(OnPaint) : View가 다시 그려질 때 호출되는 함수

그림4. NotepadView.cpp 초기화 코드
그림5. WM_CREATE 메세지 함수 추가

  • OnCreate() 소스 보기
더보기
// 최초 캐럿 위치 및 캐럿의 크기 지정
int CNotepadView::OnCreate(LPCREATESTRUCT lpCreateStruct)
{
    if (CView::OnCreate(lpCreateStruct) == -1)
        return -1;

    // TODO:  여기에 특수화된 작성 코드를 추가합니다.
    pDoc = GetDocument();    // Document 포인터 얻기

    TEXTMETRIC txtKey;

    CClientDC dc(this);        // dc 얻기

    dc.GetTextMetrics(&txtKey);    // 현 dc(View)의 폰트정보 얻기

    // 캐럿 생성(문자 평균 넓이의 절반, 문자의 높이)
    CreateSolidCaret(txtKey.tmAveCharWidth / 2, txtKey.tmHeight);

    CPoint pntTmp(0, 0);

    // 캐럿의 최초 위치 지정(0, 0)
    SetCaretPos(pntTmp);
    // 캐럿 보이기
    ShowCaret();

    return 0;
}
 

 

그림6. WM_CHAR 메세지 함수 추가

  • OnChar() 소스 보기
더보기
void CNotepadView::OnChar(UINT nChar, UINT nRepCnt, UINT nFlags)
{
    // TODO: 여기에 메시지 처리기 코드를 추가 및/또는 기본값을 호출합니다.
    CClientDC dc(this);

    TEXTMETRIC txtKey;

    dc.GetTextMetrics(&txtKey);

    // 문자 입력하는 동안 캐럿을 숨긴다
    HideCaret();

    if (nChar == VK_BACK) {    // 백 스페이스 경우
        if (!pDoc->strDB[pDoc->pline].IsEmpty()) {
            // 원래 문자열의 맨 끝을 잘라낸다.
            pDoc->strDB[pDoc->pline] = pDoc->strDB[pDoc->pline].Left(pDoc->strDB[pDoc->pline].GetLength() - 1);

            // 끝을 잘라낸 후 다시 출력
            dc.TextOut(0, pntCur.y, pDoc->strDB[pDoc->pline] + _T("    "));
        }
        else {
            if (pDoc->pline == lastline && pDoc->pline > 0) {
                pDoc->pline--;    // 줄을 하나 감소
                lastline--;
            }
        }
    }
    else if (nChar == VK_RETURN) {    // 엔터의 경우
        pDoc->pline++;    // 줄을 하나 증가
        lastline++;
    }
    else if (nChar == VK_UP) {    // 위 일경우
        pDoc->pline--;    // 줄을 하나 감소
    }
    else if (nChar == VK_DOWN) {    // 아래 일 경우
        if (pDoc->pline < lastline) {
            pDoc->pline++;
        }
    }
    else {    // 일반 문자의 경우
        pDoc->strDB[pDoc->pline] += (TCHAR)nChar;
        // 화면에 출력
        dc.TextOut(0, (int)pDoc->pline * txtKey.tmHeight, pDoc->strDB[pDoc->pline]);
    }

    // 현재 문자열이 끝나는 위치 계산
    CSize strSize;
    strSize = dc.GetTextExtent(pDoc->strDB[pDoc->pline]);

    CString convert;
    convert.Format(_T("%d"), strSize);

    // 캐럿 위치 계산
    pntCur.x = strSize.cx;
    pntCur.y = int(pDoc->pline) * txtKey.tmHeight;

    // 캐럿 위치 지정
    SetCaretPos(pntCur);

    // 캐럿 보여주기
    ShowCaret();

    CView::OnChar(nChar, nRepCnt, nFlags);
}
 
SMALL

그림7. WM_KEYUP 메세지 함수 추가

  • OnKeyUp() 소스 보기
더보기
// WM_CHAR 메세지는 출력이 불가능한 키(기능키, SHIFT, CTRL, ALT, 방향키 등)를 처리할수 없음
// 방향키를 처리하기 위해 WM_KEYUP 메세지 함수 추가
void CNotepadView::OnKeyUp(UINT nChar, UINT nRepCnt, UINT nFlags)
{
    // TODO: 여기에 메시지 처리기 코드를 추가 및/또는 기본값을 호출합니다.
    if (nChar == VK_UP) {
        if (!pDoc->pline == 0) {
            OnChar(nChar, nRepCnt, nFlags);
        }
    }
    else if (nChar == VK_DOWN) {
        OnChar(nChar, nRepCnt, nFlags);
    }
    CView::OnKeyUp(nChar, nRepCnt, nFlags);
}
 

 

그림8. WM_PAINT 메세지 함수 추가

  • OnPaint() 소스 보기
더보기
void CNotepadView::OnPaint()
{
    CPaintDC dc(this); // device context for painting
                       // TODO: 여기에 메시지 처리기 코드를 추가합니다.
                       // 그리기 메시지에 대해서는 CView::OnPaint()을(를) 호출하지 마십시오.
    TEXTMETRIC txtKey;

    dc.GetTextMetrics(&txtKey);
    for (int i = 0; i <= pDoc->pline; i++) {
        dc.TextOut(0, (int)i * txtKey.tmHeight, pDoc->strDB[i]);
    }
}
 

 

5. 실행화면

728x90
LIST