. 프로젝트 파일 열기

이제 당근이 프로젝트를 사용하는 코드를 작성해보자. 프로젝트를 열 경우 프로젝트에 소속된 파일과 묶음의 목록을 보여주고 여기서 파일을 선택할 때 열어주면 된다. 프로젝트 자체는 일반 파일과 마찬가지로 Open 함수에서 연다. 파일열기 대화상자의 필터에는 이미 프로젝트 확장자가 등록되어 있다. OpenFromFile 함수에서 확장자를 점검해보고 확장자가 dgp이면 일반 파일이 아니므로 LoadProject 함수를 호출하여 프로젝트를 연다.

 

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

{

     ....

     TCHAR ext[_MAX_EXT];

 

     _splitpath(Path,NULL,NULL,NULL,ext);

     if (stricmp(ext,".dgp")==0) {

          if (Option.bShowFileWnd==FALSE) {

               Option.bShowFileWnd=TRUE;

               Relayout();

          }

          LoadProject(Path,FALSE);

          return TRUE;

     }

 

프로젝트는 파일창에 나타나므로 파일창이 보이지 않는 상태라면 파일창을 먼저 보이도록 만들고 프로젝트를 열어야 한다. OpenFromFile 함수에서 프로젝트를 열어 주므로 프로젝트 파일을 드래그해서 떨어뜨려도 곧바로 열린다. 하지만 편집 대상 파일이 아니므로 MRU에는 등록되지 않는다. LoadProject 파일은 프로젝트를 파일창에 열어 주고 프로젝트 루트 노드를 확장한다.

프로젝트 하나가 로컬 드라이브와 같은 레벨에 있다. 별도의 프로젝트 창을 띄우는 것이 아니라 파일창의 노드 하나로 프로젝트를 보여주기 때문에 여러 개의 프로젝트를 동시에 열어 놓고 작업할 수도 있다. 프로젝트의 구조는 계층적이므로 트리 뷰의 노드 하나면 충분히 표현할 수 있고 파일과 관련된 작업들은 모두 파일창에서 한다는 점에서 일관성을 제공한다.

파일창에 프로젝트가 등록된 상태에서 프로젝트 아래의 항목을 더블클릭하면 이 파일을 열어야 한다. 이때 파일을 여는 방법은 로컬 드라이브와는 다르다. 아니 파일을 여는 방법이 다른 것이 아니라 노드로부터 파일의 경로를 구하는 방법이 다르다. GetNodePath 함수를 확장하여 로컬 드라이브뿐만 아니라 프로젝트의 파일 경로도 조사할 수 있도록 수정한다.

 

void GetNodePath(HTREEITEM pNode, TCHAR *Path)

{

     HTREEITEM tNode, tNode2, hRoot;

     TVITEM TV;

     TCHAR Text[MAX_PATH];

     TCHAR szTemp[MAX_PATH];

     TCHAR Drive[_MAX_DRIVE];

     TCHAR Dir[_MAX_DIR];

 

     TV.mask=TVIF_TEXT | TVIF_PARAM | TVIF_IMAGE;

     TV.pszText=Text;

     TV.cchTextMax=MAX_PATH;

     Path[0]=0;

 

     for (hRoot=pNode;tNode2=TreeView_GetParent(hFileTree,hRoot);hRoot=tNode2);

     TV.hItem=hRoot;

     TreeView_GetItem(hFileTree,&TV);

     if (TV.lParam) {

          _splitpath((TCHAR *)TV.lParam,Drive,Dir,NULL,NULL);

     }

 

     if (TV.iImage == DGI_PROJECT) {

          TV.hItem=pNode;

          TreeView_GetItem(hFileTree,&TV);

          if (strchr(Text,’:’)) {

              if (strnicmp(Text,"ftp",3) == 0) {

                   lstrcpy(Path,(TCHAR *)TV.lParam);

              } else {

                   lstrcpy(Path,Text);

              }

          } else {

              lstrcpy(Path,Text);

              for (tNode=pNode;;) {

                   tNode=TreeView_GetParent(hFileTree,tNode);

                   if (tNode==hRoot) {

                        wsprintf(Path,"%s%s%s",Drive,Dir,Text);

                        break;

                   }

                   TV.mask=TVIF_TEXT | TVIF_IMAGE;

                   TV.hItem=tNode;

                   TV.pszText=szTemp;

                   TreeView_GetItem(hFileTree,&TV);

                   lstrcat(szTemp,"\\");

                   lstrcat(szTemp,Path);

                   lstrcpy(Path,szTemp);

                   if (TV.iImage==DGI_FOLDER) {

                        if (strchr(szTemp,’:’)) {

                            break;

                        }

                        TV.mask=TVIF_IMAGE;

                        TV.hItem=TreeView_GetParent(hFileTree,tNode);

                        TreeView_GetItem(hFileTree,&TV);

                        if (TV.iImage != DGI_FOLDER) {

                            lstrcpy(szTemp,Path);

                            wsprintf(Path,"%s%s%s",Drive,Dir,szTemp);

                            break;

                        }

                   }

              }

          }

     } else {

          for (tNode=pNode;tNode!=NULL;) {

              TV.hItem=tNode;

              TreeView_GetItem(hFileTree,&TV);

 

              if (TreeView_GetParent(hFileTree,tNode)==NULL) {

                   Text[1]=‘:’;

                   Text[2]=0;

                   wsprintf(szTemp,"%s%s",Text,Path);

              } else {

                   wsprintf(szTemp,"\\%s%s",Text,Path);

              }

              lstrcpy(Path,szTemp);

              tNode=TreeView_GetParent(hFileTree,tNode);

          }

     }

}

 

더블클릭한 노드의 루트 노드를 먼저 구하고 루트의 이미지로부터 어떤 대상을 선택했는지 조사한다. 만약 루트가 로컬 드라이브이면 기존 방식대로 경로를 구하고 프로젝트이면 프로젝트 레코드의 경로를 구한다. 이 경로 문자열에 콜론(:)이 있으면 완전 경로이므로 이 경로를 그냥 리턴하면 된다. FTP 파일은 노드 텍스트에 모든 정보가 들어있지 않으므로 lParam에 저장되어 있는 원래 경로, 즉 사용자 ID와 비밀번호를 포함한 경로를 리턴해야 한다.

완전 경로가 아닌 경우는 두 가지로 나누어지는데 먼저 프로젝트에 직접 속한 파일의 경우를 보자. 이때는 프로젝트 경로+노드의 텍스트로 간단하게 완전 경로를 구할 수 있다. 프로젝트의 경로는 루트 노드의 lParam에 저장되어 있으므로 루트를 발견했을 때 이 문자열을 조립하여 리턴하면 된다. 하위 묶음은 어디까지나 프로젝트 내에서 파일의 정보가 저장된 위치일 뿐이지 파일의 실제 경로와는 상관이 없으므로 프로젝트 내에서의 파일 위치는 중요하지 않다.

완전 경로가 아닌 두 번째 경우는 폴더 링크내의 파일인 경우이다. 이때는 현재 노드에서 루트 노드까지 거꾸로 올라가면서 각 노드가 가진 문자열을 모두 모은다. 그러다가 중간에 절대경로를 가진 노드를 만나면 지금까지 모은 문자열을 리턴하면 된다. 만약 중간에 절대경로를 가진 노드를 만나지 못하고 폴더 링크의 루트를 만나면 프로젝트 경로+지금까지 모은 문자열을 리턴하면 된다. 자신의 이미지는 DGI_FOLDER이고 부모 노드의 이미지는 DGI_FOLDER가 아닐 때 이 노드가 바로 폴더 링크의 루트다. 다음은 어느 바람둥이의 프로젝트 파일이다.

미영, 경선은 텍스트에 콜론이 있으므로 절대경로며 이 텍스트가 원하는 경로이다. 노드가 있는 묶음의 위치는 고려하지 않아도 된다. 진숙, 한슬은 프로젝트 저장위치를 기준으로 한 상대경로이므로 프로젝트의 경로\텍스트가 원하는 경로이다. 이 경우도 노드가 속한 묶음은 경로 계산에 사용되지 않는다.

헤어진 여자는 폴더 링크이며 텍스트에 콜론이 없으므로 이 폴더는 프로젝트 경로인 C:\Letter 바로 아래에 있을 것이다. 상희 노드의 완전 경로는 이 노드에서 부모를 찾아 올라가며 문자열을 모으면 된다. 상희.txt 2\상희.txt 2002\2\상희.txt 헤어진 여자\2002\2\상희.txt 까지 일단 문자열을 모으고 이 상태에서 헤어진 여자 노드가 폴더 링크의 루트이므로 지금까지 모은 문자열 앞에 프로젝트 경로를 더하면 된다.

파일창의 윈도우 프로시저인 DGFileProc 함수에는 트리 뷰 더블클릭시 GetNodePath 함수로 구한 경로를 OpenFromFile 함수로 전달하는 코드가 이미 작성되어 있다. 따라서 GetNodePath가 프로젝트 파일의 경로만 제대로 구하면 일반 파일을 여는 과정과 동일한 방법으로 파일을 열 수 있다.