//2004년도에 모 카페에 올렸던 글입니다. ^^
브루 만년력 프로그래밍
------------------------
글. 오상문 ( sualchi@daum.net)
이전에 GNEX용 만년 달력 프로그래밍을 다룬바 있다.
이번에는 브루에서의 만년 달력 프로그래밍을 구현한 소스이다.
만년력에 대한 이론은 GNEX쪽을 참조한다.
다만, 브루용에서는 알고리즘이 개선되었다.
GNEX는 이론 설명을 위한 성격이 강했지만 실전 프로그래밍에서는
알고리즘의 개선이 필요하다.
이번 브루용 만년력은 불필요한 부분을 제거하고 실제 프로그래밍에서
써먹을 수 있는 내용으로 변경하였다.
먼저, GNEX용 만년력은 1583을 기준으로 구분하여 구현하였지만
실제 프로그래밍에서는 굳이 그럴 필요가 없다. 1년 1월 1일을 기준으로
쉽게 계산하도록 작성하고 년도 제한만 1583년 이상으로 두면 된다.
여기에서는 가상의 1년 1월 1일일 뿐이다. 실제 역사상 존재했던 그 날짜와는
차이가 있다. 다만 계산상 편의를 위하여 임시 1월 1일 1일을 설정한 것이다.
다음 매크로 함수는 지정한 년도 Y가 윤년인지 알려주는 함수이다.
#define CheckLeapYear(Y) (!(Y%400) || (!(Y%4) && (Y%100)))
그외 내용은 소스에 설명을 작성했으니 참조하기 바란다.
궁금한 사항은 꼬리글~
다음은 애플릿을 실행한 모습니다.
디자인은 신경쓰지 못해서 볼품 없지만... 그래도 만년력 프로그램이다. ^^;;
다음은 브루용 만년력의 전체 실행 소스이다.
/*===========================================================================
FILE: mycal.c
작성 : 알치 오상문
===========================================================================*/
#include "AEEModGen.h"
#include "AEEAppGen.h"
#include "AEEShell.h"
#include "AEEDisp.h"
#include "AEEStdLib.h"
#include "AEEText.h"
#include "mycal.bid"
// 1년 1월 1일과 출력할 년도의 요일 계산시 보정 값
#define BASE_WDAY 1
// 최저 기준 년도
#define BASE_YEAR 1583
// 문자 폭과 높이 기준
#define CHAR_DX 6
#define CHAR_DY 12
// 윤년인지 검사하는 매크로 함수
#define CheckLeapYear(Y) (!(Y%400) || (!(Y%4) && (Y%100)))
typedef struct _CMyCalAPP
{
AEEApplet! a;
AEERect m_rc;
uint16 m_system_year; // 시스템 현재 년도
uint16 m_system_month; // 시스템 현재 달
uint16 m_system_day; // 시스템 현재 날
uint16 m_change_year; // 선택한 년도
uint16 m_change_month; // 선택한 달
uint16 m_change_day; // 선택한 날
} CMyCalApp;
static boolean MyCal_HandleEvent(IApplet! * pi, AEEEvent eCode,
uint16 wParam, uint32 dwParam);
boolean MyCalApp_InitAppData( IApplet! *pi );
void MyCalApp_FreeAppData( IApplet! *pi );
void MyCalApp_Show( CMyCalApp *pMe );
void CharToAECHAR( const unsigned char *src, AECHAR *dst );
//-------------------------------------------------------------------
// 초기화 메인 함수
//-------------------------------------------------------------------
int AEEClsCreateInstance(AEECLSID ClsId, IShell * pIShell, IModule * po,
void ** ppObj)
{
*ppObj = NULL;
if(ClsId == AEECLSID_MYCAL)
{
if(AEEApplet!_New(sizeof(CMyCalApp), ClsId, pIShell,po,(IApplet!**)ppObj,
(AEEHANDLER)MyCal_HandleEvent, (PFNFREEAPPDATA)MyCalApp_FreeAppData)
== TRUE)
{
if( MyCalApp_InitAppData( (IApplet! *)*ppObj ))
{
return (AEE_SUCCESS);
}
else
{
IAPPLET!_Release((IApplet!*)*ppObj);
return EFAILED;
}
}
}
return (EFAILED);
}
//-------------------------------------------------------------------
// 이벤트 핸들러
//-------------------------------------------------------------------
static boolean MyCal_HandleEvent(IApplet! * pi, AEEEvent eCode, uint16 wParam, uint32 dwParam)
{
CMyCalApp *pMe = (CMyCalApp *)pi;
switch (eCode)
{
case EVT_APP_START: // 애플릿 시작
MyCalApp_Show( pMe );
return(TRUE);
case EVT_KEY: // 키 입력됨
{
switch( wParam )
{
case AVK_UP: // 년도 증가
pMe->m_change_year += 1;
MyCalApp_Show( pMe );
return(TRUE);
case AVK_DOWN: // 년도 감소
pMe->m_change_year -= 1;
if ( pMe->m_change_year < BASE_YEAR )
pMe->m_change_year = BASE_YEAR;
else
MyCalApp_Show( pMe );
return(TRUE);
case AVK_LEFT: // 월 감소
if ( pMe->m_change_month > 1 )
pMe->m_change_month -= 1;
else if (pMe->m_change_year > BASE_YEAR)
{
pMe->m_change_month = 12;
pMe->m_change_year -= 1;
}
MyCalApp_Show( pMe );
return(TRUE);
case AVK_RIGHT: // 월 증가
if ( pMe->m_change_month < 12 )
pMe->m_change_month += 1;
else
{
pMe->m_change_month = 1;
pMe->m_change_year += 1;
}
MyCalApp_Show( pMe );
return(TRUE);
}
}
case EVT_APP_STOP:
return TRUE;
default:
break;
}
return FALSE;
}
/* 날짜시간 구조체
typedef struct {
uint16 wYear; // 네자리 연도
uint16 wMonth; // 0 = 1월, 11 = 12월 ??? 1 = 1월?
uint16 wDay; // 1 ~ 31일
uint16 wHour; // 0 ~ 23시
uint16 wMinute; // 0 ~ 59분
uint16 wSecond; // 0 ~ 59초
uint16 wWeekDay; // 0 = 일, 6 = 토
} JulianType;
*/
//-------------------------------------------------------------------
// 가이드의 구조체 설명에는 0이 1월인데 에뮬레이터에서는 1이 1월이다.
// 어느 쪽이 문제일까? 현재 소스에서는 1을 1월로 처리함.
//-------------------------------------------------------------------
//-------------------------------------------------------------------
// 만년력 초기화 함수
//-------------------------------------------------------------------
boolean MyCalApp_InitAppData( IApplet! *pi )
{
CMyCalApp *pMe = (CMyCalApp *)pi;
AEEDeviceInfo di;
uint32 dwSeconds;
// 시스템 날짜 얻기
JulianType jdate;
dwSeconds = GET_SECONDS();
GET_JULIANDATE(dwSeconds, &jdate);
pMe->m_system_year = pMe->m_change_year = jdate.wYear;
pMe->m_system_month = pMe->m_change_month = jdate.wMonth; // -1 ???
pMe->m_system_day = pMe->m_change_day = jdate.wDay;
ISHELL_GetDeviceInfo( pMe->a.m_pIShell, &di );
SETAEERECT( &pMe->m_rc, 0, 0, di.cxScreen, di.cyScreen );
return TRUE;
}
// 해제할 내용이 없어서 그냥 비워둔 함수이다...
void MyCalApp_FreeAppData( IApplet! *pi )
{
// 이곳에 해제할 컨트롤 처리 ...
}
// 달력을 화면에 출력한다.
void MyCalApp_Show( CMyCalApp *pMe )
{
char sTemp[20] = {0}; // 문자 출력용 임시 char* 버퍼
AECHAR wszBuf[20] = {0}; // 문자 출력용 임시 AECHAR* 버퍼
// 평년의 경우에 할당되는 각 달의 일수 (Days[1] = 1월 일자 합, ...)
uint16 Days[13] = {0, 31, 28, 31, 30, 31, 30, 31, 31, 30, 31, 30, 31};
char * MonthStr[7] = {"일", "월", "화", "수", "목", "금", "토" };
uint16 year, month; // 년도와 달 계산을 위한 임시 변수
uint16 wday; // 요일 계산을 위한 임시 변수
uint16 i, x, y; // 출력 위치(x,y)와 순환문 임시 변수
AEERect rect; // 임시 영역 구조체
// 화면을 지운다
IDISPLAY_EraseRect( pMe->a.m_pIDisplay, &pMe->m_rc );
year = pMe->m_change_year; // 출력할 년도
month = pMe->m_change_month; // 출력할 달
// 년월일 출력하기 ...
SPRINTF(sTemp, "%d년%d월\0", year, month);
CharToAECHAR(sTemp, wszBuf);
IDISPLAY_DrawText( pMe->a.m_pIDisplay, AEE_FONT_NORMAL,
wszBuf, -1, 0, 0, &pMe->m_rc, IDF_TEXT_TRANSPARENT | IDF_ALIGN_CENTER);
// 달력에서 일요일 출력 날짜 바탕을 붉은 색으로
rect.x = 0;
rect.y = CHAR_DY*2;
rect.dx = CHAR_DX*2;
rect.dy = CHAR_DY*6;
IDISPLAY_FillRect(pMe->a.m_pIDisplay, &rect, MAKE_RGB(0xff, 0x0, 0x0));
// 달력에서 요일 출력 바탕을 녹색(연두색)으로
rect.x = 0;
rect.y = CHAR_DY;
rect.dx = CHAR_DX*14;
rect.dy = CHAR_DY;
IDISPLAY_FillRect(pMe->a.m_pIDisplay, &rect, MAKE_RGB(0x0, 0xff, 0x0));
// 달력 바탕의 격자 무늬선 그리기
for(i=0; i <= 8; i++) // 가로선
IDISPLAY_DrawHLine(pMe->a.m_pIDisplay, 0, i*CHAR_DY, CHAR_DX*14);
for(i=0; i < 8; i++) // 세로선
IDISPLAY_DrawVLine(pMe->a.m_pIDisplay, i*2*CHAR_DX, CHAR_DY, CHAR_DY*7+1);
// 출력 년도의 2월 윤달 처리... 윤달이면 29일 지정
Days[2] = (CheckLeapYear(year)) ? 29 : 28;
// 해당 년도 1월 1일의 요일을 구하는 계산 (0=일요일, 1=월요일, ...)
// BASE_WDAY은 가상의 1년1월1일 요일과의 차이 보정 값
i = year - 1; // 그 전년 도를 대상으로 먼저 계산
// 윤년이 몇 번 있었는지 구하고 요일 차이를 계산하는 식이다.
wday = BASE_WDAY + i + (i/4) - (i/100) + (i/400);
// 일주일에서 어느 요일에 해당하는지 구한다.
wday %= 7; // 해당 년도 첫 날의 요일
for (i = 1; i < month; i++) // 해당 년도의 이전 달 날짜 합
wday += Days[i];
wday %= 7; // 출력 달의 첫날 요일 계산 끝 (0=일, 1=월, ...)
// 디버깅... 구한 요일이 정확한지 테스트.... ^^;;
// SPRINTF(sTemp, "%2d", wday);
// CharToAECHAR(sTemp, wszBuf);
// IDISPLAY_DrawText( pMe->a.m_pIDisplay, AEE_FONT_NORMAL,
// wszBuf, -1, 0, 0, NULL, IDF_TEXT_TRANSPARENT);
// 요일을 출력한다.
for(i=0; i<7; i++)
{
IDISPLAY_DrawText( pMe->a.m_pIDisplay, AEE_FONT_NORMAL,
(AECHAR *)MonthStr[i], -1, i*2*CHAR_DX, CHAR_DY, NULL, IDF_TEXT_TRANSPARENT);
}
// 날짜를 제 위치에 출력한다.
for(i = 1; i <= Days[month]; i++)
{
SPRINTF(sTemp, "%2d", i);
CharToAECHAR(sTemp, wszBuf);
x = ((i + wday - 1) % 7) * 2 * CHAR_DX; // 출력할 기본 X 위치
y = ((i + wday - 1) / 7 + 2) * CHAR_DY; // 출력할 기본 Y 위치
if ((i+wday-1)%7) // 일요일이 아니면 일반 문자
IDISPLAY_DrawText( pMe->a.m_pIDisplay, AEE_FONT_NORMAL, wszBuf,
-1, x, y, NULL, IDF_TEXT_TRANSPARENT);
else // 일요일이면 굵은 문자
IDISPLAY_DrawText( pMe->a.m_pIDisplay, AEE_FONT_BOLD, wszBuf,
-1, x, y, NULL, IDF_TEXT_TRANSPARENT);
}
IDISPLAY_Update( pMe->a.m_pIDisplay ); // 지금까지 출력한 내용의 화면 갱신!
}
/*===============================================================================
기타 사용자 함수
=============================================================================== */
// char 문자열을 AECHAR 문자열로 바꾸는 함수...
void CharToAECHAR( const unsigned char *src, AECHAR *dst )
{
while( *src )
{
if( *src < 128 ) // 1바이트 문자
{
*dst = *src;
src++;
}
else // 2바이트 문자
{
MEMCPY( dst, src, 2);
src += 2;
}
dst++;
}
*dst = 0; // 2바이트 널 0x0000
}
// 소스 종료
'Mobile APP' 카테고리의 다른 글
비트별 값을 다룰 때 편리한 매크로 (Brew C, GNEX/GVM ) (0) | 2011.03.27 |
---|---|
만년력(1583년 이후부터) 알고리즘과 프로그래밍 (0) | 2011.03.27 |
[모바일C] 블랙잭 게임 만들기 (1) (0) | 2011.03.27 |
[모바일C] 블랙잭 게임 만들기 (2) (0) | 2011.03.27 |
[모바일C] 블랙잭 게임 만들기 (3) (0) | 2011.03.27 |