. 탭 관리

파일 입출력함수들은 파일을 열 때, 닫을 때, 이름을 바꿀 때 탭 관리 함수들을 적절히 호출하여 파일과 함께 파일탭도 같이 관리해야 한다. 파일탭은 결국 열린 파일의 현재 상태를 보여주는 것이기 때문에 파일과 생명을 같이 하며 따라서 파일을 관리하는 모든 코드에서 파일탭도 같이 관리해야 한다.

파일탭을 추가하는 시점은 두 군데가 있다. 첫 번째는 OpenFromFile 함수에서 파일열기에 성공했을 때이다. 파일을 열었으니 파일탭에도 어떤 파일이 열렸는지 표시해야 한다. 물론 파일열기에 실패했으면 탭도 추가할 필요가 없다.

 

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

{

     HWND hChild;

     SInfo *pSi;

     BOOL bNew=TRUE;

     TCHAR Mes[512];

    int TabIdx;

 

     ....

     if (bNew) {

          hChild=New();

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

    } else {

        TabIdx=FindFileTab(pSi->NowFile);

    }

 

     ....

 

    if (bNew) {

        AddFileTab(Path);

    } else {

        ChangeFileTab(TabIdx,Path);

    }

 

     return TRUE;

}

 

새 창을 만들었을 때는 AddFileTab 함수를 호출하여 탭을 추가하고 기존의 창에 파일을 열기만 했다면 ChangeFileTab 함수를 호출하여 탭의 텍스트만 변경하면 된다. FindFileTab 함수는 완전 경로로 대응되는 탭을 찾기 때문에 파일 이름이 바뀌기 전에 탭의 번호를 미리 찾아 놓아야 한다.

파일탭을 추가하는 두 번째 시점은 새로운 문서창을 만들었을 때이다. 비록 이름없음 n이라는 이름을 가지지만 대응되는 문서창을 가지고 있으므로 파일탭을 만들어야 한다. 새 문서창을 생성하고 파일탭에 추가하는 다음 함수를 새로 추가한다.

 

HWND NewChild()

{

     HWND hChild;

     SInfo *pSi;

 

     g_NewNo++;

     hChild=New();

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

     AddFileTab(pSi->NowFile);

     return hChild;

}

 

이 함수는 새 문서창을 생성하고 이 창의 편집파일 이름(이름없음 n)을 조사한 후 탭에 추가한다. New 함수는 이제 새로운 윈도우를 만드는 함수로 의미가 바뀌게 되며 NewChild 함수가 새 문서를 만드는 함수가 된다. New 함수에 있는 g_NewNo++; 문장은 이제 삭제하도록 하자. 이 변수는 새 문서의 일련번호이지 새 윈도우의 일련번호가 아니므로 NewChild에서만 증가시켜야 한다. New 함수를 호출하여 새 문서를 만드는 함수들은 New 대신 NewChild를 호출하도록 수정한다. 모두 4군데가 있다. 우선 파일/새 파일 명령으로 새 문서를 만들 때이다.

 

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

{

     ....

     case IDM_FILE_NEW:

        NewChild();

          break;

 

새 창을 만드는 곳은 OnTimer에도 있다. 시작할 때(StartAction) 새 문서 열기 옵션이 선택되어 있다거나, 또는 편집하던 문서 열기 옵션이 선택되어 있더라도 마지막 편집파일이 없을 경우에는 새 문서를 여는데 이때도 New 대신 NewChild를 호출하도록 수정한다.

 

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

{

     ....

          switch (Option.StartAction)

          {

          case 0:

              break;

          case 1:

           NewChild();

              break;

          case 2:

              nEditing=SHRegReadInt(SHCU,KEY"Editing","Num",0);

              SHRegReadString(SHCU,KEY"Editing","Active","",Path,MAX_PATH);

              if (nEditing && lstrlen(Path) != 0) {

                   OpenFromFile(Path);

              } else {

               NewChild();

              }

              break;

 

마지막으로 새 창을 만드는 아주 특수한 경우는 검색결과창에서 검색결과를 새 창으로 보낼 때이다.

 

LRESULT CALLBACK DGOutputProc(HWND hWnd,UINT iMessage,WPARAM wParam,LPARAM lParam)

{

     switch(iMessage) {

     ....

     case WM_COMMAND:

          ....

          case IDM_OUTPUT_SENDNEW:

           NewChild();

              SendOutputToWindow();

              break;

 

새 문서를 만드는 기능에 파일탭을 추가하는 기능이 들어가게 되었고 그래서 이 두 동작을 같이 수행하는 NewChild 함수를 만들었다. NewChild=New+AddFileTab이라고 할 수 있는데 그렇다면 왜 New 함수를 직접 수정하여 AddFileTab 호출을 포함하도록 하지 않고 새로운 함수를 만들었을까 하는 궁금함이 생길 것이다. New 함수를 직접 고치면 New 함수를 호출하는 곳을 고치지 않아도 되지 않을까?

이렇게 해야 하는 이유는 원래 의미 그대로의 New를 필요로 하는 곳도 있기 때문이다. 즉 새 문서를 만드는 것이 아니라 새 창만 만드는 경우가 아직 남아 있기 때문에 파일탭에 추가는 하지 않고 새 창만 만드는 함수가 여전히 필요하며 그 역할을 New 함수가 계속 하고자 하는 것이다. OpenFromFile 함수는 문서를 열기 전에 새 창을 먼저 만들고 OpenFileToChild를 바로 호출하므로 여기서 필요한 함수는 분명히 NewChild가 아니라 New이다.

만약 New AddFileTab 호출을 포함시킨다면 OpenFromFile 함수는 같은 파일을 탭에 두 번 등록하게 될 것이다. NewChild를 새로 만드는 대신 NewWithoutAddFileTab 함수를 새로 만드는 것도 해결책이 될 수 있다.

파일탭을 삭제할 시점은 편집창이 파괴될 때 한 군데뿐이다. 문서창이 WM_DESTROY 메시지를 받았을 때 파일탭을 삭제하면 된다. 문서가 파괴되면 대응되는 파일탭도 같이 삭제되어야 하며 이때 탭의 lParam에 할당되어 있던 메모리도 해제된다.

 

LRESULT CALLBACK DGChildProc(HWND hWnd,UINT iMessage,WPARAM wParam,LPARAM lParam)

{

     ....

     case WM_DESTROY:

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

        DeleteFileTab(pSi->NowFile);

          delete pSi;

          g_ChildNum--;

          if (g_ChildNum==0) {

              SendMessage(g_hMDIClient,WM_MDISETMENU,(WPARAM)hMenu2,NULL);

              Mru.ChangeMenu(GetSubMenu(GetSubMenu(hMenu2,0),3));

              DrawMenuBar(g_hFrameWnd);

              if (Option.bShowStatus) {

                   SetStatusPart();

              }

          }

          return 0;

     }

     return(DefMDIChildProc(hWnd,iMessage,wParam,lParam));

}

 

파일탭을 수정할 곳도 한 군데뿐이다. SaveAs로 파일 이름을 바꾸어 저장할 때 탭 텍스트도 변경된 파일 이름으로 바꾼다. 파일 이름이 바뀌기 전에 대상 탭을 먼저 찾아 놓아야 한다.

 

BOOL SaveAs(HWND hChild)

{

     OPENFILENAME OFN;

     TCHAR lpstrFile[MAX_PATH]="";

    int TabIdx;

 

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

    TabIdx=FindFileTab(pSi->NowFile);

 

     ....

 

     Mru.AddMRU(OFN.lpstrFile);

     lstrcpy(pSi->NowFile,OFN.lpstrFile);

     SetWindowText(hChild,pSi->NowFile);

 

    ChangeFileTab(TabIdx,pSi->NowFile);

     return TRUE;

}

 

파일탭의 추가, 삭제, 수정 작업은 파일탭의 보임/숨김(bShowFileTab) 옵션과는 상관없이 항상 해야 한다. 설사 파일탭이 숨겨져 있는 상태라 하더라도 언제든지 사용자가 보이도록 옵션을 바꿀 수 있기 때문에 현재 열린 파일 목록을 항상 정확하게 유지하도록 관리되어야 한다.