.속성 편집

새로 생성되는 모든 도형은 전역 변수 Opt 속성을 따르므로 Opt 멤버를 변경하면 이후 생성될 도형의 속성을 선택할 있다. 또한 이미 그려진 도형의 속성도 바꿀 있어야 하는데 여러 개의 속성을 한꺼번에 보여주고 변경해야 하므로 대화 상자를 사용하는 것이 편리하다. 리소스에는 도형의 속성 변경에 사용할 있는 대화상자 템플리트가 이미 작성되어 있다.

왼쪽에는 선과 면의 속성을 입력받는 컨트롤이 있고 오른쪽에는 글꼴의 속성을 입력받는 컨트롤이 있는데 텍스트 툴은 아직 추가되지 않았으므로 일단 왼쪽 컨트롤만 사용하도록 하자. 색상값을 입력받는 콤보 박스는 오너 드로우 속성을 가지는데 미리 정해진 색상중 하나를 선택하도록 한다. 전역 색상 테이블을 다음과 같이 정의한다.

 

COLORREF arColor[]={-1,RGB(0,0,0),RGB(255,255,255),RGB(255,0,0),RGB(0,255,0),

   RGB(0,0,255),RGB(255,255,0),RGB(255,0,255),RGB(0,255,255),RGB(64,64,64),

   RGB(128,128,128),RGB(192,192,192)};

 

콤보 박스는 색상들을 그래픽으로 보여 주며 사용자는 하나를 고를 있다. 대화상자에서 업다운 컨트롤을 사용하므로 commctrl.h 인클루드해야 하며 OnCreate에서는 InitCommonControls 함수를 호출해야 한다. 프로젝트 설정의 링크 탭에서 comctl32.lib 연결하도록 하자.

 

#include <windows.h>

#include <commctrl.h>

#include "resource.h"

....

LRESULT Main_OnCreate(HWND hWnd,WPARAM wParam,LPARAM lParam)

{

   InitCommonControls();

   ....

 

속성 편집 대화상자 하나로 전역 속성과 개별 도형의 속성을 같이 편집해야 하므로 대화상자는 파라미터를 통해 조정 대상을 전달받아 결과를 다시 돌려 주도록 해야 한다. 대화상자로 전달되는 파라미터는 모든 속성을 포함하고 있는 DObject 구조체의 포인터여야 한다. 대화상자 프로시저를 다음과 같이 작성한다.

 

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

{

   static DObject *Obj;

   LPMEASUREITEMSTRUCT lpmis;

   LPDRAWITEMSTRUCT lpdis;

   HBRUSH ColorBrush, OldBrush;

   COLORREF Color;

   int i;

 

   switch(iMessage) {

   case WM_INITDIALOG:

      for (i=0;i<sizeof(arColor)/sizeof(arColor[0]);i++) {

          SendDlgItemMessage(hDlg,IDC_CBLINECOLOR,CB_ADDSTRING,0,(LPARAM)arColor[i]);

          SendDlgItemMessage(hDlg,IDC_CBPLANECOLOR,CB_ADDSTRING,0,(LPARAM)arColor[i]);

      }

      SendDlgItemMessage(hDlg,IDC_SPLINEWIDTH,UDM_SETRANGE,0,MAKELONG(10,0));

 

      Obj=(DObject *)lParam;

      SetDlgItemInt(hDlg,IDC_EDLINEWIDTH,Obj->LineWidth,FALSE);

      for (i=0;i<sizeof(arColor)/sizeof(arColor[0]);i++) {

          if (arColor[i] == Obj->LineColor) {

             break;

          }

      }

      SendDlgItemMessage(hDlg,IDC_CBLINECOLOR,CB_SETCURSEL,i,0);

      for (i=0;i<sizeof(arColor)/sizeof(arColor[0]);i++) {

          if (arColor[i] == Obj->PlaneColor) {

             break;

          }

      }

      SendDlgItemMessage(hDlg,IDC_CBPLANECOLOR,CB_SETCURSEL,i,0);

      return TRUE;

   case WM_MEASUREITEM:

      lpmis=(LPMEASUREITEMSTRUCT)lParam;

      lpmis->itemHeight=24;

      return TRUE;

   case WM_DRAWITEM:

      lpdis=(LPDRAWITEMSTRUCT)lParam;

 

      if (lpdis->itemState & ODS_SELECTED) {

          FillRect(lpdis->hDC, &lpdis->rcItem, GetSysColorBrush(COLOR_HIGHLIGHT));

      } else {

          FillRect(lpdis->hDC, &lpdis->rcItem, GetSysColorBrush(COLOR_WINDOW));

      }

 

      Color=(COLORREF)SendMessage(lpdis->hwndItem, CB_GETITEMDATA, lpdis->itemID, 0);

      if (Color == (COLORREF)-1) {

          ColorBrush=(HBRUSH)GetStockObject(NULL_BRUSH);

      } else {

          ColorBrush=CreateSolidBrush(Color);

      }

      OldBrush=(HBRUSH)SelectObject(lpdis->hDC, ColorBrush);

      Rectangle(lpdis->hDC,lpdis->rcItem.left+5,lpdis->rcItem.top+2,

          lpdis->rcItem.right-5, lpdis->rcItem.bottom-2);

      SelectObject(lpdis->hDC, OldBrush);

      if (Color == (COLORREF)-1) {

          SetTextAlign(lpdis->hDC,TA_CENTER);

          SetBkMode(lpdis->hDC,TRANSPARENT);

          TextOut(lpdis->hDC,(lpdis->rcItem.right+lpdis->rcItem.left)/2,

             lpdis->rcItem.top+4,"투명",4);

      } else {

          DeleteObject(ColorBrush);

      }

      return TRUE;

   case WM_COMMAND:

      switch (wParam) {

      case IDOK:

          Obj->LineWidth=GetDlgItemInt(hDlg,IDC_EDLINEWIDTH,NULL,FALSE);

          i=SendDlgItemMessage(hDlg,IDC_CBLINECOLOR,CB_GETCURSEL,0,0);

          Obj->LineColor=arColor[i];

          i=SendDlgItemMessage(hDlg,IDC_CBPLANECOLOR,CB_GETCURSEL,0,0);

          Obj->PlaneColor=arColor[i];

          EndDialog(hDlg,IDOK);

          return TRUE;

      case IDCANCEL:

          EndDialog(hDlg,IDCANCEL);

          return TRUE;

      }

      break;

   }

   return FALSE;

}

 

개의 색상 콤보 박스에 색상표의 색상을 항목 데이터로 추가해 놓고 선의 색상을 조정하는 스핀 버튼의 범위를 0~10으로 설정한다. WM_INITDIALOG에서 lParam으로 전달되는 DObject 포인터를 받아 들여 객체의 내용대로 컨트롤을 초기화했다. 선의 굵기는 단순한 정수값이므로 SetDlgItemInt 에디트에 출력하면 되고 색상은 색상표와 일치하는 항목을 찾아 콤보 박스에 선택한다. 입력받은 설정값을 컨트롤에 그대로 보여 주는 것이다.

개의 오너 드로우 콤보 박스를 가지고 있으므로 WM_MEASUREITEM 메시지와 WM_DRAWITEM 메시지를 처리하고 있는데 모든 색상의 높이는 24픽셀로 동일하다. WM_DRAWITEM에서는 선택 상태일 파란색 배경을 칠하고 그렇지 않을 때는 흰색 배경을 칠한 항목 ID로부터 항목 데이터로 저장된 색상의 브러시를 만든 사각형을 그렸다. , -1 경우는 투명색임을 텍스트로 표시한다. 개의 콤보 박스에 대한 오너 드로우 처리를 하고 있는데 콤보 박스의 모양이 완전히 일치하므로 어떤 컨트롤이 그리기를 요청했는지는 점검할 필요없이 항목 ID만으로 그리기를 수행하면 된다.

WM_COMMAND에서는 IDOK 버튼을 누를 편집된 결과를 lParam으로 전달된 DObject 구조체에 다시 채워서 돌려준다. 대화상자를 호출하는 쪽에서는 편집 대상인 DObject 객체의 포인터를 파라미터로 전달한다. 우선 편집/속성 명령이 선택되었을 메인 윈도우에서 대화상자를 다음과 같이 호출한다.

 

LRESULT Main_OnCommand(HWND hWnd,WPARAM wParam,LPARAM lParam)

{

   switch(LOWORD(wParam)) {

   ....

   case IDM_SHAPE_PROPERTY:

      if (NowSel == -1) {

          DialogBoxParam(g_hInst, MAKEINTRESOURCE(IDD_PROPERTY),

             hWnd,PropertyDlgProc,(LPARAM)&Opt);

      } else {

          SendMessage(hCanvas,WM_COMMAND,MAKEWPARAM(IDM_POPUP_PROPERTY,0),0);

      }

      break;

 

선택 도형이 없다면 전역 옵션을 변경하라는 뜻이므로 전역 옵션 객체인 Opt 번지를 파라미터로 전달했다. 대화상자는 Opt 내용대로 컨트롤을 초기화하고 사용자가 옵션을 조정하면 Opt 속성값을 직접 바꿀 것이다. 전역 속성이 변경되었을 메인 윈도우가 추가 동작을 필요는 없으므로 대화상자의 리턴값은 점검할 필요가 없다. 전역 속성은 다음 그릴 도형에만 영향을 미치며 이미 그려진 도형에 대해서는 아무런 영향력을 발휘하지 않는다. 변경된 전역 옵션은 다음 도형을 그릴 사용된다.

선택 도형이 있는 상태에서 메뉴를 선택했으면 선택된 도형의 속성만 변경하라는 뜻인데 이때는 캔버스에게 명령을 전달하여 대신 처리하도록 했다. 캔버스는 자신만의 팝업 메뉴를 가지고 있으며 팝업 메뉴에서 속성을 편집할 있다. 캔버스의 OnCommand에도 다음 코드를 추가한다.

 

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

{

   switch(LOWORD(wParam)) {

   ....

   case IDM_POPUP_PROPERTY:

      if (NowSel != -1) {

          if (DialogBoxParam(g_hInst, MAKEINTRESOURCE(IDD_PROPERTY),

             hWnd,PropertyDlgProc,(LPARAM)arObj[NowSel])==IDOK) {

             InvalidateRect(hWnd,NULL,TRUE);

          }

      }

      break;

   }

   return 0;

}

 

선택된 도형의 포인터를 바로 전달하여 도형의 속성을 편집하도록 했다. 속성 편집을 완료했으면 변경된 도형을 다시 그려야 하므로 작업 영역을 무효화한다. 편집을 취소했으면 아무 동작도 필요가 없다.