. 통지 메시지

상태란에는 편집에 도움이 될만한 여러 가지 정보들이 출력된다. 간단한 도움말, 현재 편집하고 있는 위치, 편집하고 있는 파일에 대한 상세한 정보, 작업 결과 등이 상태란에 표시되며 사용자들은 항상 이 정보를 참조한다. 상태란에 표시되는 정보는 보통 파일의 상태를 나타내기 때문에 ApiEdit 컨트롤의 상태를 나타내는 경우가 많다.

상태란이 컨트롤의 상태를 출력하는 방법에는 두 가지가 있는데 첫 번째는 주기적으로 ApiEdit에게 상태를 질문하는 것이고 두 번째는 ApiEdit가 상태가 바뀔 때 호스트에게 변화 사실을 알려 주는 방법이다. 첫 번째 방법은 반응성도 좋지 않을 뿐더러 실행속도를 감소시키기 때문에 바람직하지 않다. 여기서는 두 번째 방법을 사용할 것이다.

호스트가 상태란에 정확한 정보를 출력하기 위해서는 컨트롤의 변화 시점을 알아야 하는데 이런 용도로는 통상 통지 메시지(Notification Message)가 사용된다. 컨트롤은 자신에게 변화가 있을 때 부모 윈도우에게 미리 약속된 메시지를 보내며 이 메시지를 통해 호스트는 컨트롤에 변화가 생겼음을 알게 된다. 표준 컨트롤인 에디트는 자신이 편집되면 EN_CHANGE 메시지를 보내며 콤보박스는 선택이 변경되면 CBN_SELCHANGE 통지 메시지를 보낸다.

ApiEdit도 컨트롤이므로 자신의 변화를 부모 윈도우에게 통지 메시지로 알리도록 해보자. 통지 메시지로 사용할 메시지 ID ApiEdit.h에 다음과 같이 선언한다.

 

#define AEN_CHANGE 1

#define AEN_MOVE 2

#define AEN_INSMODE 3

#define AEN_CHGMODI 4

 

4개의 통지 메시지가 일단 정의되었는데 이 개수는 필요에 따라 더 늘어날 수도 있다. 통지 메시지의 ID는 통상 컨트롤의 이름과 N을 접두로 사용하므로 AEN 접두를 붙여주었다. 각 통지 메시지의 의미는 다음과 같다.

 

통지 메시지

의미

AEN_CHANGE

문서가 변경되었다.

AEN_MOVE

캐럿이 이동되었다.

AEN_INSMODE

덮어쓰기 모드가 변경되었다.

AEN_CHGMODI

문서의 변경 여부가 바뀌었다.

 

ApiEdit는 이런 변화가 발생했을 때 부모 윈도우에게 통지 메시지를 보내야 한다. 표준 컨트롤의 통지 메시지는 WM_COMMAND로 전달되며 wParam의 하위 워드에 윈도우의 ID, 상위 워드에 통지 코드를, lParam에 윈도우의 핸들을 전달하므로 ApiEdit도 그렇게 하도록 하자. 다음 함수는 부모 윈도우에게 WM_COMMAND 메시지를 보내되 wParam, lParam에 윈도우 ID와 통지 코드를 전달한다.

 

void CApiEdit::SendNotify(int code)

{

     SendMessage(GetParent(hWnd),WM_COMMAND,

          MAKEWPARAM(GetWindowLong(hWnd,GWL_ID),code),(LPARAM)hWnd);

}

 

WM_COMMAND wParam, lParam은 이미 용도가 확정되었기 때문에 여분의 정보를 실을 공간은 없다. 단순히 사건이 발생했음을 알릴 뿐이다. ApiEdit는 각각의 변화가 발생할 때 SendNotify 함수를 호출하여 부모 윈도우에게 통지 메시지를 보내야 한다.

문서가 변경되었다는 의미의 AEN_CHANGE 통지 메시지는 두 군데에서 발생한다. 다음 두 함수에서 이 메시지를 부모 윈도우에게 보낸다.

 

void CApiEdit::Insert(int nPos, TCHAR *str, BOOL bRec/*=TRUE*/)

{

     ....

    SendNotify(AEN_CHANGE);

}

 

void CApiEdit::Delete(int nPos, int nCount, BOOL bRec/*=TRUE*/)

{

     ....

    SendNotify(AEN_CHANGE);

}

 

캐럿이 이동되는 AEN_MOVE메시지는 각종 키에서 수시로 발생하지만 캐럿이 이동될 때는 항상 SetCaret이 호출되므로 이 함수에서만 통지 메시지를 보내주면 된다. , 캐럿의 위치가 정말로 바뀌었을 때만 이 통지 메시지를 보내야 한다. 포커스의 변경 등은 캐럿을 표시하거나 숨기기는 하지만 편집 위치가 바뀐 것은 아니므로 통지 메시지를 보낼 필요가 없다.

 

void CApiEdit::SetCaret(BOOL bUpdatePrevX/*=TRUE*/, BOOL bScrollToCaret/*=TRUE*/)

{

     ....

     if (bScrollToCaret)

        SendNotify(AEN_MOVE);

     ....

}

 

덮어쓰기 모드가 변경될 때인 AEN_INSMODE OnKey에서 VK_INSERT키가 <Shift> <Ctrl>키와 조합되지 않고 단독으로 눌러질 때 보내진다. , bOvr 변수에 변화가 있을 때마다 이 통지 메시지를 보내면 된다.

 

void CApiEdit::OnKey(HWND hWnd, UINT vk, BOOL fDown, int cRepeat, UINT flags)

{

     ....

     case VK_INSERT:

          if (bShift) {

              SendMessage(hWnd,WM_COMMAND,MAKEWPARAM(IDM_PASTE,0),0);

          } else if (bControl) {

              SendMessage(hWnd,WM_COMMAND,MAKEWPARAM(IDM_COPY,0),0);

          } else {

              bOvr = !bOvr;

           SendNotify(AEN_INSMODE);

              SetCaret();

          }

 

bOvr 변수를 먼저 변경한 후 AEN_INSMODE 통지 메시지를 보내야 한다. 왜냐하면 AEN_INSMODE 통지 메시지는 덮어쓰기 모드가 바뀌었다는 것을 호스트에게 알려 주기만 할 뿐 어떻게 바뀌었다는 것은 알려주지 못하기 때문이다. 그래서 호스트는 통상 이 메시지를 받자 마자 현재의 덮어쓰기 모드를 조사하는데 이때 정확한 상태를 조사하기 위해서는 bOvr이 먼저 변경되어 있어야 한다. AEN_INSMODE덮어쓰기 모드가 변경되었다는 통지 메시지인데 만약 bOvr을 변경하기 전에 이 메시지를 보낸다면 이는 덮어쓰기 모드가 곧 변경될 것이다로 의미가 바뀌게 된다.

AEN_CHGMODI 메시지는 bModified가 변경될 때 보내진다. 이 메시지는 문서가 변경되었을 때 보내지는 AEN_CHANGE와는 약간 다르다. 문서가 변경되더라도 실행취소에 의해 bModified는 다시 FALSE가 될 수 있기 때문이다. SetModified 함수를 다음과 같이 수정한다.

 

void CApiEdit::SetModified(BOOL aModi)

{

    if (bModified != aModi) {

        bModified=aModi;

        SendNotify(AEN_CHGMODI);

    }

     if (aModi==FALSE) {

          SaveModified();

     }

}

 

bModified와 새로 대입될 aModi값이 실제로 다를 때만 이 통지 메시지를 보내도록 하였다. SetModified 함수는 문서가 조금이라도 편집되면 항상 호출되는데 그때마다 AEN_CHGMODI 통지 메시지를 보낼 필요는 없다. AEN_CHGMODI 통지 메시지는 문서의 변경상태가 실제로 바뀌었다는 뜻이지 bModified에 값이 대입되었다는 뜻이 아니다. 이 메시지도 마찬가지로 bModified값을 먼저 변경한 후 통지 메시지를 보내야 한다.

참고로 InitDoc 함수는 SetModified를 호출하지 않고 바로 bModified FALSE로 대입하여 초기화한다. 그 이유는 초기화중에는 통지 메시지를 보내지 않도록 하기 위해서이다. 만약 InitDoc에서도 SetModified를 호출한다면 초기화중에 AEN_CHGMODI가 날라가고 그러면 Dangeun은 아직 초기화도 덜된 ApiEdit 컨트롤에게 정보를 요구하게 되는데 이는 치명적인 에러의 원인이 된다.