삽질하는플머

달력 컨트롤

Lazarus/KOL-CE
라자루스를 어디에 쓸까 고민하다가... 음력에 대한 공부도 할 겸 "달 구경" 프로그램을 짬짬이 만들고 있다.
날짜 선택을 위해 달력 컨트롤을 올려놓으려고 했더니... 응? 델파이의 TDateTimePicker에 해당하는 TKOLDateTimePicker 는 있는데 TMonthCalendar에 해당하는 컨트롤이 없다. 허걱~~ fpc의 wince 유니트를 열어보니 DateTimePicker와 MonthCal에 사용되는 ICC_DATE_CLASSES 가 주석으로 막혀있네~ (라자루스 0.9.24 의 LCL 윈CE 인터페이스는 현재 베타버전이다.)

그렇다면 KOL의 TKOLDateTimePicker 는 뭘까 싶어 다시 KOL.pas 를 뒤져보니... 여기에 다시 재정의 되어있었다. 그러나 애석하게도 달력 컨트롤에 대한 코드는 찾아볼 수 없었다.

처음에는 간단히 LCL로 DLL을 만들어 쓰려고 했었는데... 위에서 언급했듯이 아직 윈CE 인터페이스가 베타라 arm-wince 로 달력 컨트롤을 컴파일하면 에러가 발생한다. 뭐 여차저차해서 만들기로 했고, 아직 내공부족으로 KOL 컴포넌트화 할 수는 없었지만 그럭저럭 쓸만하기에 방법을 정리 해 본다.

달력 컨트롤에 관련된 상수들은 evc와 함께 설치한 STANDARDSDK 5.0 에 정의되어 있으며 "C:\Program Files\Windows CE Tools\wce500\STANDARDSDK_500\Include\...\commctrl.h" 에서 찾을 수 있다. 이 파일을 번역하려고 슬쩍 훑어보니 다행히 상수 및 구조체가 윈도 32와 99% 동일하다. 그렇다면?? 델파이의 VCL을 가져와 쓰자.


VCL은 오픈소스가 아니므로 여기에 옮길 수 없다. 델파이 5.0을 기준으로 과정만 소개 해 본다.


1. 먼저 라자루스의 File->NewUnit 를 선택, 새로 만들 달력 컨트롤이 담길 유니트를 만들어 준다. 이름은 적당히.

2. uses 섹션에는 Windows, Messages, KOL, CommCtrl 유니트를 적어넣는다.

3. ...\Delphi5\source\rtl\win\commctrl.pas 파일을 열고 다음 내용들을 복사 해 온다.

3-1. 6371번 줄 (MONTHCAL CONTROL) 부터 6664번 줄 (MonthCal_GetUnicodeFormat) 까지 interface 섹션에 복사 해 넣는다.

3-2. 8508번 줄 (MonthCal_GetCurSel 함수) 부터 8623번 줄 까지 implementation 섹션에 복사 해 넣는다.

3-3. MonthCal_SetUnicodeFormat, MonthCal_GetUnicodeFormat 함수는 wince에서는 동작하지 않는다. 이 두 함수의 선언 및 구현부는 {$ifdef win32} 지시자로 감싸둔다.

4. KOL의 TControl 기반의 달력 컨트롤을 생성할 수 있도록 다음과 같이 NewMonthCalendar 함수를 만들어 준다.

function WndProcMonthCalendarNotify( Self_: PControl; var Msg: TMsg; var Rslt: Integer ): Boolean;
begin
  Result := false;
end;

function NewMonthCalendar( AParent: PControl): PControl;
const
  CS_OFF = {$ifdef win32}CS_OWNDC or CS_CLASSDC or {$endif}CS_PARENTDC or CS_GLOBALCLASS or
           CS_VREDRAW or CS_HREDRAW;
begin
  DoInitCommonControls( ICC_DATE_CLASSES );
  Result := _NewCommonControl( AParent, MONTHCAL_CLASS,
         WS_CHILD or WS_VISIBLE or WS_TABSTOP,
         TRUE,  nil );
  Result.SetSize( 191, 154 );
  Result.AttachProc( WndProcMonthCalendarNotify );
{$ifdef wince}
  Result.Perform(CCM_SETVERSION, COMCTL32_VERSION, 0);
{$endif wince}
end;



작업은 이것으로 끝. 달력을 생성해 폼 위에 올리는 코드는 다음과 같다.

TCalendarForm = ....
...
private
  fCalendar: PControl;
...

procedure TCalendarForm.KOLForm1FormCreate(Sender: PObj);
begin
  fCalendar := NewMonthCalendar(Form);
end;



달력에 날짜를 지정하거나 읽어올 때는 델파이에서 복사해 온 MonthCal_SetCurSel, MonthCal_GetCurSel 를 쓰면 편하다. 한가지, KOL의 SystemTime2DateTime은 델파이와 달리 1899년12월30일이 아닌 0년 0월 0일을 기준으로 한다. 아직 분석도 다 안끝났고 무엇보다 귀찮으니 그냥 SysUtils에 정의된 SystemTimeToDateTime() 을 쓰자.


function TCalendarForm.GetDateTime: TDateTime;
var
  pst: TSystemTime;
begin
  MonthCal_GetCurSel(fCalendar.Handle, pst);
  Result := SysUtils.SystemTimeToDateTime(pst);
end;

procedure TCalendarForm.SetDateTime(const AValue: TDateTime);
var
  pst: TSystemTime;
begin
  SysUtils.DateTimeToSystemTime(AValue, pst);
  MonthCal_SetCurSel(fCalendar.Handle, pst);
end;




폰트 크기를 50으로 주고 480*800 에뮬레이터에서 돌려본 화면은 짜잔~~ 멋지지 않은가!!







3월 23일 릴리즈 된 0.9.26.2 버전의 윈CE/LCL 인터페이스에 캘린더가 추가되었다고 한다.
그러나 이 팁은 KOL에서는 아직까지 유효하다~~ ^^