. 읽기전용 속성 관리

ApiEdit는 읽기전용 속성을 지원하는데 이 속성을 참조하기는 하지만 직접 변경하지는 않는다. 적당한 조건이 되면 읽기전용 속성을 설정하여 파일이 편집되는 것을 금지하는 것은 호스트가 알아서 할 일이다. 파일을 열어 보고 이 파일이 편집 불가능한 상태이거나 또는 단순히 파일 내용을 보여주고만 싶다거나 또는 사용자의 명시적인 요구가 있을 때 읽기전용 상태로 만들어야 한다. ApiEdit는 호스트에서 이 속성을 자유롭게 액세스할 수 있도록 함수만 제공하는데 다음 두 멤버함수를 추가하도록 하자.

 

class CApiEdit

{

     ....

     BOOL GetReadOnly() { return bReadOnly; }

     void SetReadOnly(BOOL aReadOnly);

 

bReadOnly 플래그의 상태를 조사하거나 변경하는 함수이다. SetReadOnly 함수는 다음과 같이 작성되어 있다.

 

void CApiEdit::SetReadOnly(BOOL aReadOnly)

{

     bReadOnly=aReadOnly;

}

 

읽기전용 플래그의 상태만 바꾸면 편집을 금지하는 일은 ApiEdit의 편집코드들이 알아서 한다. 호스트는 파일 메뉴의 읽기전용 항목이 선택되면 이 함수들을 호출하여 읽기전용 플래그를 토글시킨다.

 

void OnCommand(HWND hWnd,WPARAM wParam,LPARAM lParam)

{

     HWND hActive;

    SInfo *pSi;

 

     hActive=(HWND)SendMessage(g_hMDIClient,WM_MDIGETACTIVE,0,NULL);

     if (hActive==NULL) {

          if (TestNeedActive(LOWORD(wParam)))

              return;

    } else {

        pSi=(SInfo *)GetWindowLong(hActive,0);

    }

 

     switch(LOWORD(wParam)) {

     ....

    case IDM_FILE_READONLY:

        pSi->Ae.SetReadOnly(!pSi->Ae.GetReadOnly());

        break;

 

활성 차일드의 ApiEdit 컨트롤을 구하기 위해 먼저 SInfo 구조체의 포인터를 pSi에 구하고 pSi->Ae.SetReadOnly 함수를 호출하여 읽기전용 상태를 토글하였다. 다음은 읽기전용 상태를 메뉴항목에 표시하도록 해보자. OnInitMenu 함수를 만들고 다음 코드를 작성한다.

 

void OnInitMenu(HWND hWnd,WPARAM wParam,LPARAM lParam)

{

     HMENU hMenu;

     HWND hActive;

     SInfo *pSi;

 

     hMenu=(HMENU)wParam;

     hActive=(HWND)SendMessage(g_hMDIClient,WM_MDIGETACTIVE,0,NULL);

     if (hActive) {

          pSi=(SInfo *)GetWindowLong(hActive,0);

          if (pSi->Ae.GetReadOnly()) {

              CheckMenuItem(hMenu, IDM_FILE_READONLY, MF_BYCOMMAND | MF_CHECKED);

          } else {

              CheckMenuItem(hMenu, IDM_FILE_READONLY, MF_BYCOMMAND | MF_UNCHECKED);

          }

     }

}

 

읽기전용 상태를 체크 표시로 보여주되 단, 활성 차일드가 없을 때는 읽기전용 메뉴가 보이지 않으므로 이 처리를 할 필요가 없다. 이제 파일/읽기전용 메뉴항목을 통해 파일의 편집 가능 여부를 확인할 수 있고 변경할 수 있게 되었다. 실행중에 읽기전용 상태를 변경하는 것 외에 파일을 열 때도 이 상태를 지정할 수 있는데 파일열기 대화상자의 아래쪽에 있는 읽기전용으로 열기 체크박스가 이런 용도로 사용된다.

파일을 열 때 이 체크박스가 선택되어 있으면 읽기전용 상태로 읽어야 할 것이다. 또한 사용자의 명시적인 의사 표현이 없더라도 파일이 읽기전용 속성을 가지고 있으면 이 파일도 읽기전용으로 열려야 한다. 파일열기 대화상자는 Open 함수에서 관리하므로 이 함수에서 대화상자를 닫은 직후에 사용자가 체크박스를 선택했는지 확인해보고 그 결과를 OpenFromFile 함수로 알려줘야 한다.

 

void Open()

{

     ....

     if (GetOpenFileName(&OFN)) {

          p=lpstrFile;

          lstrcpy(Dir,p);

          p=p+lstrlen(Dir)+1;

          if (*p==0) {

           OpenFromFile(Dir, (OFN.Flags & OFN_READONLY));

          } else {

              for (;*p;) {

                   wsprintf(Path,"%s\\%s",Dir,p);

                   p=p+lstrlen(p)+1;

               OpenFromFile(Path, (OFN.Flags & OFN_READONLY));

              }

          }

     ....

 

OPENFILENAME 구조체의 Flags OFN_READONLY 플래그가 설정되어 있으면 파일열기 대화상자에서 사용자가 읽기전용을 요구한 것이다. OpenFromFile 함수는 다음과 같이 수정된다. 함수의 원형이 바뀌었으므로 원형 선언부도 같이 수정해야 한다.

 

BOOL OpenFromFile(TCHAR *Path, BOOL bReadOnly/*=FALSE*/)

{

     ...

    if (bReadOnly || (GetFileAttributes(Path) & FILE_ATTRIBUTE_READONLY)) {

        pSi->Ae.SetReadOnly(TRUE);

    }

     return TRUE;

}

 

열고자 하는 파일명과 읽기전용 상태 플래그를 인수로 전달받되 이 인수는 디폴트값으로 FALSE를 가지도록 하였다. 왜냐하면 파일열기 대화상자를 통하지 않은 경우(:쉘 오픈)는 이 옵션이 없기 때문이다. 이 인수가 TRUE이거나 파일이 읽기전용 속성을 가지고 있으면 파일을 열자 마자 SetReadOnly 함수를 호출하여 읽기전용 상태로 만든다.

읽기전용 속성을 가진 파일을 열었더라도 파일/읽기전용 메뉴항목을 선택하여 쓰기 가능하게 만든 후 편집하는 것은 허용된다. ApiEdit는 디스크 기반이 아니므로 메모리 버퍼로 이미 읽어들인 텍스트를 편집하는 것은 가능하기 때문이다. 하지만 이렇게 편집한 파일은 같은 이름으로는 저장할 수 없으며 다른 이름으로 저장해야 한다.

조금 더 코드를 작성하면 읽기전용 속성을 해제한 후 저장하고 다시 읽기전용 속성을 부여하는 방식으로 억지로 저장할 수도 있다. 또는 아예 새 이름으로 저장할 수 있도록 파일 저장 대화상자를 보여주는 것도 괜찮은 방법이다. 하지만 읽기전용이라는 파일 속성을 만든 목적이 함부로 변경하지 말아야 할 파일임을 분명히 표시하자는 뜻이므로 이를 존중하여 에러 메시지만 출력하도록 하였다. 지나치게 친절을 베푸는 것보다는 왜 파일 쓰기가 안되는지 분명히 알려 주고 사용자 스스로 문제를 해결하도록 하는 것이 더 좋은 정책이라고 생각한다.