. 보기

두 번째 페이지인 보기 페이지는 프로그램의 다양한 모양에 대한 옵션을 가지고 있으며 이 페이지를 통해 사용자가 편집기를 원하는 모양으로 직접 변경할 수 있다.

많은 옵션들이 있는데 툴바 옵션을 제외하고는 모두 ApiEdit 컨트롤의 옵션이며 지금까지의 실습을 통해 이미 작성했던 기능들이다. 이 대화상자의 프로시저는 다음과 같이 작성한다.

 

BOOL CALLBACK ViewDlgProc(HWND hDlg,UINT iMessage,WPARAM wParam,LPARAM lParam)

{

     HWND hCon;

     int i,idx;

     int tint;

     TCHAR szFace[32];

 

     switch(iMessage)

     {

     case WM_INITDIALOG:

          hCon=GetDlgItem(hDlg,IDC_LINEHEIGHT);

          SendMessage(hCon,CB_ADDSTRING,0,(LPARAM)"100");

          SendMessage(hCon,CB_ADDSTRING,0,(LPARAM)"120");

          SendMessage(hCon,CB_ADDSTRING,0,(LPARAM)"150");

          SendMessage(hCon,CB_ADDSTRING,0,(LPARAM)"200");

          SendMessage(hCon,CB_ADDSTRING,0,(LPARAM)"250");

          SendMessage(hCon,CB_ADDSTRING,0,(LPARAM)"300");

 

          hCon=GetDlgItem(hDlg,IDC_FONTSIZE);

          SendMessage(hCon,CB_ADDSTRING,0,(LPARAM)"8");

          SendMessage(hCon,CB_ADDSTRING,0,(LPARAM)"9");

          SendMessage(hCon,CB_ADDSTRING,0,(LPARAM)"10");

          SendMessage(hCon,CB_ADDSTRING,0,(LPARAM)"11");

          SendMessage(hCon,CB_ADDSTRING,0,(LPARAM)"12");

          SendMessage(hCon,CB_ADDSTRING,0,(LPARAM)"15");

          SendMessage(hCon,CB_ADDSTRING,0,(LPARAM)"20");

          SendMessage(hCon,CB_ADDSTRING,0,(LPARAM)"30");

 

          hCon=GetDlgItem(hDlg,IDC_CARETWIDTH);

          SendMessage(hCon,CB_ADDSTRING,0,(LPARAM)"1");

          SendMessage(hCon,CB_ADDSTRING,0,(LPARAM)"2");

          SendMessage(hCon,CB_ADDSTRING,0,(LPARAM)"3");

          SendMessage(hCon,CB_ADDSTRING,0,(LPARAM)"4");

 

          hCon=GetDlgItem(hDlg,IDC_SEL);

          SendMessage(hCon,CB_ADDSTRING,0,(LPARAM)"숨김");

          SendMessage(hCon,CB_ADDSTRING,0,(LPARAM)"흐리게 표시");

          SendMessage(hCon,CB_ADDSTRING,0,(LPARAM)"항상 표시");

 

          hCon=GetDlgItem(hDlg,IDC_CUR);

          SendMessage(hCon,CB_ADDSTRING,0,(LPARAM)"강조하지 않음");

          SendMessage(hCon,CB_ADDSTRING,0,(LPARAM)"박스로 표시");

          SendMessage(hCon,CB_ADDSTRING,0,(LPARAM)"밑줄로 표시");

          SendMessage(hCon,CB_ADDSTRING,0,(LPARAM)"음영으로 표시");

          SendMessage(hCon,CB_ADDSTRING,0,(LPARAM)"음영과 박스로 표시");

          SendMessage(hCon,CB_ADDSTRING,0,(LPARAM)"마진에 표시");

 

          hCon=GetDlgItem(hDlg,IDC_WRAP);

          SendMessage(hCon,CB_ADDSTRING,0,(LPARAM)"한글 글자, 영문 글자");

          SendMessage(hCon,CB_ADDSTRING,0,(LPARAM)"한글 글자, 영문 단어");

          SendMessage(hCon,CB_ADDSTRING,0,(LPARAM)"한글 단어, 영문 단어");

 

          SendDlgItemMessage(hDlg,IDC_FONTFACE,CB_ADDSTRING,0,(LPARAM)"==시스템==");

          for (i=0;i<FontNum;i++) {

              SendDlgItemMessage(hDlg,IDC_FONTFACE,CB_ADDSTRING,0,(LPARAM)arFont[i].lfFaceName);

          }

 

          return TRUE;

     case WM_COMMAND:

          switch (LOWORD(wParam)) {

          case IDC_FONTFACE:

              AdjustDlgUI(1,hDlg);

          case IDC_FONTSIZE:

          case IDC_LINEHEIGHT:

          case IDC_CARETWIDTH:

          case IDC_SEL:

          case IDC_CUR:

          case IDC_WRAP:

              switch (HIWORD(wParam)) {

              case CBN_SELCHANGE:

              case CBN_EDITCHANGE:

                   PropSheet_Changed(GetParent(hDlg),hDlg);

                   break;

              }

              return TRUE;

          case IDC_MARGIN:

              AdjustDlgUI(1,hDlg);

          case IDC_LINENUM:

          case IDC_WORDWRAP:

          case IDC_HANGUL:

          case IDC_SHOWENTER:

          case IDC_SHOWTAB:

          case IDC_SHOWSPACE:

          case IDC_HIDECURLINE:

          case IDC_TOOLBARBIG:

          case IDC_TOOLBARTEXT:

          case IDC_NOFIRSTSPACE:

              PropSheet_Changed(GetParent(hDlg),hDlg);

              return TRUE;

          case IDC_RIGHTWRAP:

          case IDC_COLMARK:

              if (HIWORD(wParam) == EN_CHANGE) {

                   if (bEditByCode==FALSE) {

                        PropSheet_Changed(GetParent(hDlg),hDlg);

                   }

              }

              return TRUE;

          }

          break;

     case WM_NOTIFY:

          switch (((LPNMHDR)lParam)->code) {

          case PSN_SETACTIVE:

              g_StartPage=1;

              SetDlgItemInt(hDlg,IDC_LINEHEIGHT,NewOption.LineRatio,FALSE);

 

              CheckDlgButton(hDlg,IDC_MARGIN,NewOption.bShowMargin ? BST_CHECKED:BST_UNCHECKED);

              CheckDlgButton(hDlg,IDC_LINENUM,NewOption.bShowLineNum ? BST_CHECKED:BST_UNCHECKED);

              CheckDlgButton(hDlg,IDC_WORDWRAP,NewOption.bWrap ? BST_CHECKED:BST_UNCHECKED);

              CheckDlgButton(hDlg,IDC_HANGUL,NewOption.Hangul ? BST_CHECKED:BST_UNCHECKED);

              CheckDlgButton(hDlg,IDC_SHOWENTER,NewOption.bShowEnter ? BST_CHECKED:BST_UNCHECKED);

              CheckDlgButton(hDlg,IDC_SHOWTAB,NewOption.bShowTab ? BST_CHECKED:BST_UNCHECKED);

              CheckDlgButton(hDlg,IDC_SHOWSPACE,NewOption.bShowSpace ? BST_CHECKED:BST_UNCHECKED);

              CheckDlgButton(hDlg,IDC_HIDECURLINE,NewOption.bHideCurLine ? BST_CHECKED:BST_UNCHECKED);

              CheckDlgButton(hDlg,IDC_TOOLBARBIG,NewOption.bToolBarBig ? BST_CHECKED:BST_UNCHECKED);

              CheckDlgButton(hDlg,IDC_TOOLBARTEXT,NewOption.bToolBarText ? BST_CHECKED:BST_UNCHECKED);

              CheckDlgButton(hDlg,IDC_NOFIRSTSPACE,NewOption.bNoFirstSpace ? BST_CHECKED:BST_UNCHECKED);

 

              if (NewOption.logfont.lfHeight == 0) {

                   idx=0;

                   SetDlgItemInt(hDlg,IDC_FONTSIZE,10,FALSE);

                   EnableWindow(GetDlgItem(hDlg,IDC_FONTSIZE),FALSE);

              } else {

                   idx=SendDlgItemMessage(hDlg,IDC_FONTFACE,CB_FINDSTRING,-1,

                        (LPARAM)NewOption.logfont.lfFaceName);

                   SetDlgItemInt(hDlg,IDC_FONTSIZE,NewOption.logfont.lfHeight,FALSE);

              }

              SendDlgItemMessage(hDlg,IDC_FONTFACE,CB_SETCURSEL,idx,0);

 

              SendDlgItemMessage(hDlg,IDC_SEL,CB_SETCURSEL,NewOption.HideSelType,0);

              SendDlgItemMessage(hDlg,IDC_CUR,CB_SETCURSEL,NewOption.nShowCurLine,0);

              SendDlgItemMessage(hDlg,IDC_WRAP,CB_SETCURSEL,NewOption.nWrap-1,0);

              SendDlgItemMessage(hDlg,IDC_CARETWIDTH,CB_SETCURSEL,NewOption.CaretWidth-1,0);

              bEditByCode=TRUE;

              SetDlgItemInt(hDlg,IDC_RIGHTWRAP,NewOption.RightWrap,FALSE);

              SetDlgItemInt(hDlg,IDC_COLMARK,NewOption.ColMark,FALSE);

              bEditByCode=FALSE;

              AdjustDlgUI(1,hDlg);

              return TRUE;

          case PSN_APPLY:

              ApplyNow();

              return TRUE;

          case PSN_KILLACTIVE:

              tint=GetDlgItemInt(hDlg,IDC_LINEHEIGHT,NULL,FALSE);

              if (tint < 100 || tint > 1000) {

                   MessageBox(hDlg,"줄간은 100~1000사이의 값을 지정해야 합니다","알림",MB_OK);

                   SetWindowLong(hDlg,DWL_MSGRESULT,TRUE);

                   return TRUE;

              }

 

              tint=GetDlgItemInt(hDlg,IDC_FONTSIZE,NULL,FALSE);

              if (tint < 6 || tint > 72) {

                   MessageBox(hDlg,"글꼴의 크기는 6~72사이의 값을 지정해야 합니다","알림",MB_OK);

                   SetWindowLong(hDlg,DWL_MSGRESULT,TRUE);

                   return TRUE;

              }

 

              NewOption.LineRatio=GetDlgItemInt(hDlg,IDC_LINEHEIGHT,NULL,FALSE);

              NewOption.bShowMargin=IsDlgButtonChecked(hDlg,IDC_MARGIN) ? TRUE:FALSE;

              NewOption.bShowLineNum=IsDlgButtonChecked(hDlg,IDC_LINENUM) ? TRUE:FALSE;

              NewOption.bWrap=IsDlgButtonChecked(hDlg,IDC_WORDWRAP) ? TRUE:FALSE;

              NewOption.Hangul=IsDlgButtonChecked(hDlg,IDC_HANGUL) ? TRUE:FALSE;

              NewOption.bShowEnter=IsDlgButtonChecked(hDlg,IDC_SHOWENTER) ? TRUE:FALSE;

              NewOption.bShowTab=IsDlgButtonChecked(hDlg,IDC_SHOWTAB) ? TRUE:FALSE;

              NewOption.bShowSpace=IsDlgButtonChecked(hDlg,IDC_SHOWSPACE) ? TRUE:FALSE;

              NewOption.bHideCurLine=IsDlgButtonChecked(hDlg,IDC_HIDECURLINE) ? TRUE:FALSE;

              NewOption.bToolBarBig=IsDlgButtonChecked(hDlg,IDC_TOOLBARBIG) ? TRUE:FALSE;

              NewOption.bToolBarText=IsDlgButtonChecked(hDlg,IDC_TOOLBARTEXT) ? TRUE:FALSE;

              NewOption.bNoFirstSpace=IsDlgButtonChecked(hDlg,IDC_NOFIRSTSPACE) ? TRUE:FALSE;

 

              idx=SendDlgItemMessage(hDlg,IDC_FONTFACE,CB_GETCURSEL,0,0);

              if (idx==0) {

                   NewOption.logfont.lfHeight=0;

              } else {

                   SendDlgItemMessage(hDlg,IDC_FONTFACE,CB_GETLBTEXT,idx,(LPARAM)szFace);

                   for (i=0;i<FontNum;i++) {

                        if (lstrcmp(szFace,arFont[i].lfFaceName) == 0)

                             break;

                   }

                   NewOption.logfont=arFont[i];

                   NewOption.logfont.lfHeight=GetDlgItemInt(hDlg,IDC_FONTSIZE,NULL,FALSE);

              }

 

              NewOption.HideSelType=SendDlgItemMessage(hDlg,IDC_SEL,CB_GETCURSEL,0,0);

              NewOption.nShowCurLine=SendDlgItemMessage(hDlg,IDC_CUR,CB_GETCURSEL,0,0);

              NewOption.nWrap=SendDlgItemMessage(hDlg,IDC_WRAP,CB_GETCURSEL,0,0)+1;

              NewOption.CaretWidth=SendDlgItemMessage(hDlg,IDC_CARETWIDTH,CB_GETCURSEL,0,0)+1;

              NewOption.RightWrap=GetDlgItemInt(hDlg,IDC_RIGHTWRAP,NULL,FALSE);

              NewOption.ColMark=GetDlgItemInt(hDlg,IDC_COLMARK,NULL,FALSE);

              return TRUE;

          }

          break;

     }

     return FALSE;

}

 

함수의 구조는 일반 페이지와 거의 비슷하다. 아니 비슷한 정도가 아니라 완전히 같은 꼴이다. 일반 페이지와 비교해 볼 때 대화상자의 컨트롤과 관리하는 옵션의 종류가 다른 것 외에는 별다른 차이점이 없다. 콤보박스를 많이 사용하기 때문에 WM_INITDIALOG의 초기화 코드가 길며 글꼴 콤보박스는 Config 함수에서 미리 조사해놓은 폰트 목록(arFont)으로 초기화하여 이름으로 글꼴을 선택할 수 있도록 했다.

PSN_KILLACTIVE에서는 컨트롤로부터 값을 읽기 전에 줄간과 글꼴의 크기가 유효한지를 먼저 점검한다. 줄간은 100~1000까지가 유효하며 글꼴은 6~72까지만 인정한다. 만약 유효하지 못한 값을 입력했을 때는 FALSE를 리턴하여 이 페이지를 떠나지 못하도록 하였다. 대화상자의 통지 메시지가 리턴값을 리턴할 때는 return문으로 리턴하는 것이 아니라 DWL_MSGRESULT에 리턴값을 대입해야 함을 유의하자.

이 대화상자는 우리가 앞에서 이미 만들었던 친숙한 옵션을 처리하고 있으므로 이 옵션들이 어떻게 표시되고 변경되는지를 살펴 보도록 하자. 대표적으로 체크박스인 마진영역 표시 옵션과 콤보박스인 비활성화시 선택영역 옵션의 경우만 보면 나머지 옵션들도 같은 방식으로 이해할 수 있을 것이다.

마진 영역 표시 옵션은 체크박스(IDC_MARGIN)로 표현되기 때문에 별도의 초기화가 필요없으며 따라서 WM_INITDIALOG에서는 이 컨트롤에 대해서는 아무것도 하지 않는다. PSN_SETACTIVE에서는 이 컨트롤과 대응되는 NewOption.bShowMargin 변수값을 읽어 체크박스에 상태를 표시하며 이때는 CheckDlgButton 함수가 사용되었다. NewOption.bShowMargin TRUE이면 이 컨트롤을 BST_CHECKED로 만들고 아니면 BST_UNCHECKED로 만들어 사용자에게 현재 마진영역 표시 옵션이 어떻게 설정되어 있는지를 보여준다.

대화상자가 닫힐 때인 PSN_KILLACTIVE에서는 반대로 IDC_MARGIN 컨트롤의 체크 상태를 확인하여 NewOption.bShowMargin변수값을 변경한다. 사용자가 체크박스를 체크했으면 이 변수를 TRUE로 변경하고 체크하지 않았으면 FALSE로 변경한다. 물론 이 옵션을 건드리지 않았다면 원래의 값을 그대로 유지할 것이다.

WM_COMMAND에서는 사용자가 이 체크박스를 조작할 때 AdjustDlgUI 함수를 호출하여 이 옵션에 종속되는 줄번호 보기 체크박스(IDC_LINENUM)을 관리한다. 마진영역이 보이는 상태에서만 줄번호 보기 체크박스를 변경할 수 있다. 사용자가 체크박스를 건드렸으면 옵션에 변화가 생겼으므로 적용 버튼을 활성화한다. 그래서 AdjustDlgUI 호출문 다음에 break가 생략되어 있고 PropSheet_Changed 함수도 같이 호출된다.

다음은 비활성화시 선택영역 옵션을 처리하는 IDC_SEL 콤보박스를 보자. WM_INITDIALOG에서 콤보박스에 선택 가능한 항목들을 추가한다. 이때 콤보박스의 항목 순서는 HideSelType 옵션값과 일치시킴으로써 콤보박스의 인덱스가 곧 옵션값과 같도록 하였다. PSN_SETACTIVE에서 NewOption.HideSelType 변수값으로 콤보박스의 인덱스를 초기화하여 현재 선택 상태를 보여주었으며 PSN_KILLACTIVE에서 콤보박스의 인덱스를 NewOption.HideSelType 변수로 다시 대입하였다.

WM_COMMAND에서는 CBN_SELCHANGE, CBN_EDITCHANGE 통지 메시지를 받았을 때만 적용 버튼을 활성화한다. 이 두 메시지 외의 다른 콤보박스 통지 메시지, 예를 들어 드롭 다운 리스트박스가 열렸다는 CBN_DROPDOWN이나 포커스를 받았다는 CBN_SETFOCUS 등은 옵션값의 변경과는 무관하므로 이때는 적용 버튼을 활성화해서는 안된다. IDC_SEL 콤보박스에 대한 처리가 이렇게 간단한 이유는 HideSelType 옵션과 이 콤보박스의 항목 인덱스가 일치하며 이 콤보박스에 CBS_SORT 스타일이 없기 때문이다.

반면 글꼴 콤보박스는 옵션 자체가 정수가 아니고 lfHeight 0이면 시스템 글꼴이라는 특수값이 존재하기 때문에 옵션을 보여주고 되돌려 받는 과정이 훨씬 더 복잡하다. 글꼴 이름으로 콤보박스의 인덱스를 찾아야 하며 PSN_KILLACTIVE에서는 선택한 글꼴 이름으로부터 arFont 배열의 인덱스를 먼저 찾고 이 인덱스의 폰트를 NewOption.logfont에 대입해야 한다. 이 콤보박스는 CBS_SORT 스타일을 가지므로 콤보박스의 인덱스로부터 바로 arFont 배열의 인덱스를 찾을 수가 없다. 또한 lfHeight 0인 경우를 항상 따로 점검할 필요도 있다.