. CMru

MRU의 핵심인 문자열 배열 클래스가 완성되었다. 다음은 이 클래스를 사용하여 최근 파일 목록을 관리하는 CMru 클래스를 만들어보자. Util.h CMru 클래스를 다음과 같이 선언한다.

 

class CMru

{

private:

     CHistory arMru;

     HMENU hMenu;

     TCHAR Key[MAX_PATH];

 

public:

     UINT MenuID;

     int MaxMru;

 

     CMru();

     ~CMru();

     BOOL InitMRU(TCHAR *aKey, HMENU aMenu, UINT aMenuID, int aMaxMru);

     BOOL ChangeMenu(HMENU aMenu);

     void ChangeMaxMru(int aMaxMru);

     void LoadMRU();

     void SaveMRU();

     void AddMRU(TCHAR *Path);

     void RemoveMRU(int nIndex);

     void ArrangeMenu();

     void GetFilePath(int nIndex,TCHAR *buf);

};

 

arMru 객체는 최근 읽은 파일의 경로를 저장하는 문자열 배열이다. 경로는 문자열이므로 CHistory 클래스로 관리하기에 적당하다. MaxMru 멤버는 목록의 최대 크기를 지정한다. 이 값이 너무 작으면 실용성이 떨어지고 또 너무 크면 메뉴 리스트가 길어져서 별로 보기에 좋지 않다. 프로그램의 성격에 맞게 적당한 값을 선택하되 10 정도가 적당한 크기이다.

최근 파일의 목록은 팝업메뉴에 나타나는데 hMenu는 목록 출력에 사용될 팝업메뉴의 핸들이다. 프로그램은 MRU로 사용할 팝업메뉴를 미리 만들어 두고 그 핸들을 CMru 클래스에게 가르쳐 줘야 한다. CMru는 이 팝업메뉴에 목록을 출력하는데 목록에 해당하는 각 메뉴항목의 ID MenuID 멤버변수부터 연속적인 ID를 할당한다. 시작 ID도 프로그램이 CMru에게 제공해야 하는데 이 ID 이후 MaxMru 개수의 ID가 연속적으로 비어 있어야 한다.

MRU 목록은 프로그램 종료시 레지스트리에 저장되는데 Key 멤버는 저장할 레지스트리 경로를 가리킨다. CMru MaxMru 값을 전달받아 히스토리 객체를 초기화하고 hMenu, MenuID를 전달받아 메뉴를 구성하고 Key가 지정하는 위치에 목록을 저장한다. 상기 네 값은 MRU 구현에 꼭 필요하므로 이 클래스를 사용하는 프로그램이 제공해야 한다. 다음은 비주얼 스튜디오의 최근 파일 메뉴이다.

팝업메뉴 하나가 MRU 목록을 위해 할당되어 있으며 이 메뉴 아래에 최근 파일의 경로가 메뉴항목으로 등록되어 있다. 각 메뉴항목은 약속된 연속적인 ID를 가지고 있을 것이며 최대 개수도 미리 정해져 있다. 멤버변수들 외에 생성자, 파괴자를 비롯한 몇 가지 멤버함수들이 포함되어 있는데 각 함수들의 구현 코드를 Util.cpp에 작성해보자.

 

CMru::CMru()

{

}

 

CMru::~CMru()

{

}

 

BOOL CMru::InitMRU(TCHAR *aKey, HMENU aMenu, UINT aMenuID, int aMaxMru)

{

     MaxMru=aMaxMru;

     arMru.Init(MAX_PATH+64,MaxMru);

     lstrcpy(Key,aKey);

     MenuID=aMenuID;

     return ChangeMenu(aMenu);

}

 

BOOL CMru::ChangeMenu(HMENU aMenu)

{

     if (aMenu) {

          hMenu=aMenu;

          ArrangeMenu();

          return TRUE;

     }

     return FALSE;

}

 

void CMru::ChangeMaxMru(int aMaxMru)

{

     arMru.ChangeHeight(aMaxMru);

     ArrangeMenu();

}

 

void CMru::LoadMRU()

{

     int i;

     TCHAR SubKey[32];

     TCHAR Path[MAX_PATH];

 

     arMru.num=SHRegReadInt(SHCU,Key,"nFiles",0);

     arMru.num=min(arMru.num, MaxMru);

     for (i=0;i<arMru.num;i++) {

          wsprintf(SubKey,"File%d",i);

          SHRegReadString(SHCU,Key,SubKey,"",Path,MAX_PATH);

          arMru.Set(i,Path);

     }

     ArrangeMenu();

}

 

void CMru::SaveMRU()

{

     int i;

     TCHAR SubKey[32];

 

     SHRegWriteInt(SHCU,Key,"nFiles",arMru.num);

     for (i=0;i<arMru.num;i++) {

          wsprintf(SubKey,"File%d",i);

          SHRegWriteString(SHCU,Key,SubKey,arMru.Get(i));

     }

}

 

void CMru::AddMRU(TCHAR *Path)

{

     arMru.Add(Path);

     ArrangeMenu();

}

 

void CMru::RemoveMRU(int nIndex)

{

     arMru.Delete(nIndex);

     ArrangeMenu();

}

 

void CMru::ArrangeMenu()

{

     int i;

     TCHAR szItem[MAX_PATH+12];

 

     if (arMru.num) {

          while (DeleteMenu(hMenu,0,MF_BYPOSITION));

     }

 

     for (i=0;i<arMru.num;i++) {

          if (i < 9) {

              wsprintf(szItem,"&%c %s",i+1+’0’,arMru.Get(i));

          } else {

              wsprintf(szItem,"&%c %s",i-9+’A’,arMru.Get(i));

          }

          AppendMenu(hMenu,MF_STRING,MenuID+i,szItem);

     }

}

 

void CMru::GetFilePath(int nIndex,TCHAR *buf)

{

     lstrcpy(buf,arMru.Get(nIndex));

}

 

생성자와 파괴자는 아무 것도 하지 않으며 빈둥빈둥 놀고 있다. CMru의 실제 초기화 함수는 InitMRU 함수인데 왜 생성자 대신 이 함수를 따로 정의하는가 하면 MaxMru는 호스트의 설정값이므로 레지스트리에서 별도로 읽어야 그 실제값을 알 수 있기 때문이다. CMru 객체를 동적으로 생성한다면 몰라도 생성자 호출 시점에서는 이 값을 알 수가 없다.

InitMRU 함수는 arMru.Init를 호출하여 목록 버퍼를 초기화한다. MAX_PATH+64만큼의 문자열이 MaxMru만큼 할당되고 등록 개수는 0으로 초기화한다. 즉 이 객체가 만들어질 때 목록은 비어있다. 파일의 경로는 MAX_PATH 길이이면 충분히 저장할 수 있는데 여기에 64를 더한 이유는 FTP 파일의 경우 사용자 ID, 패스워드, 포트 번호 같은 추가적인 정보가 더 필요하기 때문이다.

InitMRU 함수는 이 객체가 동작하는데 필수적인 네 가지 주요 정보를 전달받아 MaxMru, hMenu, Key, MenuID 멤버에 저장한다. 이 정보는 MRU 목록 출력 및 저장에 반드시 필요하므로 이 객체를 사용하는 프로그램은 반드시 InitMRU 함수를 호출하여 초기화해야 한다. 메뉴의 ID와 최대 개수는 ChangeMenu, ChangeMaxMru 함수로 언제든지 변경할 수 있다.

LoadMRU 함수는 레지스트리에 저장되어 있는 최근 파일 목록을 읽는다. 저장된 파일의 개수를 먼저 arMru.num에 읽어들이고 그 개수만큼 파일 경로를 읽어 arMru 배열에 직접 대입한다. 각 파일들의 경로는 File0, File1, File2 등의 키에 저장된다. SaveMRU 함수는 반대로 목록을 레지스트리에 저장한다. 저장할 때도 개수를 먼저 저장하고 arMru 배열을 저장한다.

MRU에 새 파일을 추가하거나 삭제하는 것은 아주 쉽다. CHistory 객체의 Add, Delete 함수만 호출하면 된다. , 파일 목록에 변화가 생겼으므로 ArrangeMenu 함수를 호출하여 메뉴를 갱신해야 한다.

ArrangeMenu 함수는 arMru 파일 목록을 hMenu 팝업메뉴에 출력한다. 일단 기존에 등록되어 있는 모든 메뉴항목을 다 지우고 AppendMenu 함수로 메뉴항목을 추가하였다. 각 항목의 ID는 프로그램이 제공한 MenuID값부터 시작하여 연속적으로 증가한다. 만약 등록된 문자열의 개수인 arMru.num 0이면 즉, 목록이 비어 있으면 아무 것도 하지 않으므로 비어있음이라는 디폴트 메뉴항목만 보이게 된다. 각 메뉴항목에 1~9, A~Z까지 단축키를 달아 주었다.

GetFilePath 함수는 arMru 객체가 유지하는 문자열 배열의 nIndex 번째 항목을 읽는 함수인데 arMru 객체가 private 액세스 속성을 가지기 때문에 클래스 외부에서 읽을 수 있도록 하기 위해 제공되는 함수이다.