하늘소프트SDK API 문서

Win32 샘플 5 - 다중 윈도우/다중 뷰


앞의 예제인 단일 윈도우 다중 뷰를 확장하여 다중 윈도우를 여는 예를 보인다. 각 윈도우가 가지는 기능은 동일하며 메뉴의 "새 창 추가"를 실행하면 창이 하나 더 추가된다. 메모리의 전자해도 데이터 는 모든 창이 공유하기 때문에 창이 늘어난다고 해도 해도 데이터를 위한 메모리를 더 사용하지는 않는다.

스크린샷 원본 미보관

보관된 원본 이미지가 없어 표시할 수 없습니다. 위 코드와 설명을 참고해 주십시오.

win32_sample05_01.png

[그림 5-1] 다중 윈도우 어플리케이션의 새 창 열기 메뉴


스크린샷 원본 미보관

보관된 원본 이미지가 없어 표시할 수 없습니다. 위 코드와 설명을 참고해 주십시오.

win32_sample05_02.png

[그림 5-2] 새 창 열기의 결과로 생성된 윈도우


코드


윈도우 맵

윈도우 정보나 윈도우 내의 뷰 정보를 다루는 클래스는 앞에서 살펴본 예제인 단일 윈도우/다중 뷰와 동일하며 윈도우 핸들을 키로 윈도우 정보를 밸류로 가지는 맵이 전역으로 선언되어 다수개의 윈도우에 대한 정보를 저장한다.

map<HWND, CWndInfo> g_wndInfo;

윈도우 개수 관리

생성된 창의 개수를 관리할 변수를 전역으로 설정한다. 생성될 각 창마다 동일한 변수가 동일한 값을 가지고 있게 된다.

int g_cntWindow = 0;

새 창 추가

메뉴의 커맨드를 처리하는 WM_COMMAND 메시지의 이벤트 핸들러에 해당되는 onCommand()에서 IDM_NEW_WINDOW 처리부에서 새 창을 다음과 같이 생성한다.

LRESULT onCommand(HWND hWnd, UINT message, WPARAM wParam, LPARAM lParam) {
	int wmId    = LOWORD(wParam);
	int wmEvent = HIWORD(wParam);
	// 메뉴의 선택 영역을 구문 분석합니다.
	switch (wmId) {
        ...
		case IDM_NEW_WINDOW :
			{
				HWND hWnd = CreateWindow(
						g_szWindowClass,
						g_szTitle,
						WS_OVERLAPPEDWINDOW,
						CW_USEDEFAULT,
						0,
						CW_USEDEFAULT,
						0,
						NULL,
						NULL,
						g_hInstance,
						NULL
				);
				ShowWindow(hWnd, SW_NORMAL );
				UpdateWindow(hWnd);
			}
			break;
        ...
	}

	return 0;
}

창이 생성될 때의 처리

CreateWindow()에 의해 창이 생성될 때 WM_CREATE 메시지가 전달되는데 onCreate()에서 다음과 같이 새로운 창의 윈도우 핸들로 윈도우 맵의 아이템을 추가하고 그 아래에서 hencCreate()를 사용하여 새로 생성된 창에 대한 HGLRC를 생성한다.

LRESULT onCreate(HWND hWnd, UINT message, WPARAM wParam, LPARAM lParam) {
	TCHAR buf[256];

	LPCREATESTRUCT cs = (LPCREATESTRUCT )lParam;

	g_cntWindow++;
	CWndInfo &info = g_wndInfo[ hWnd ];
	info.m_hwnd = hWnd;
	info.m_cntView = CNT_INITIAL_VIEW;
	_stprintf_s( buf, 256, _T( "HNS ENC 0x%X" ), hWnd );
	SetWindowText( hWnd, buf );
	HDC hdc = GetDC( hWnd );
	hencCreate( hWnd );
    ...
}

생성된 창의 제거

창을 닫을 때 WM_DESTROY 메시지가 전달되는 데 hencDelete() 함수를 사용하여 창과 연결된 HGLRC를 삭제하며 생성된 창의 개수 값이 0일 때는 PostQuitMessage() 함수를 실행하여 인스턴스를 종료한다.

LRESULT onDestory(HWND hWnd, UINT message, WPARAM wParam, LPARAM lParam) {
	hencDelete( hWnd );

	if( --g_cntWindow <= 0 )
		PostQuitMessage(0);
	return  0;
}

전체 코드


WinMain.cpp

// SAMPLE.cpp : 응용 프로그램에 대한 진입점을 정의합니다.
//

#include "stdafx.h"

#define MAX_LOADSTRING 100
#define CNT_INITIAL_VIEW		1

// 전역 변수:
HINSTANCE g_hInstance;								// 현재 인스턴스입니다.
TCHAR g_szTitle[MAX_LOADSTRING];					// 제목 표시줄 텍스트입니다.
TCHAR g_szWindowClass[MAX_LOADSTRING];			// 기본 창 클래스 이름입니다.
int g_cntWindow = 0;

map<HWND,CWndInfo> g_wndInfo;

void initComboSet( HWND hwnd, int id, const char *szSet[] ) {
	HWND hwndCtrl = GetDlgItem( hwnd, id );
	for( int i = 0; szSet[ i ] != NULL; i++ )
		ComboBox_AddString( hwndCtrl, szSet[ i ] );
}

void viewRedrawAll() {
	for_each(
		g_wndInfo.begin(),
		g_wndInfo.end(),
		[]( pair<HWND,CWndInfo> wndinfo ) {
			InvalidateRect( wndinfo.first, NULL, FALSE );
		}
	);
}

int setTopmost( HWND hwnd, int istop ) {
	if( SetWindowPos( hwnd, istop ? HWND_TOPMOST : HWND_NOTOPMOST,
			0, 0, 0, 0,
			SWP_NOSIZE | SWP_NOMOVE ) )
		return 0;
	return -1;
}

void loadMap() {
	hencLoad( _T( "SHP\\CNTRY92.SENC" ) );
	CLoadENC loadEnc;
	loadEnc.setMessage( EMI_CAPTION, _T( "Loading. . ." ) );

	hencLoadFileListFromCatalogFile( _T( "SENC\\CATALOG.SENC" ), loadEnc.m_listFn );
	dlgProgress( g_wndInfo.begin()->first, &loadEnc );
}

int APIENTRY _tWinMain(HINSTANCE hInstance, HINSTANCE hPrevInstance, LPTSTR lpCmdLine, int nCmdShow ) {
	UNREFERENCED_PARAMETER(hPrevInstance);
	UNREFERENCED_PARAMETER(lpCmdLine);

	debugviewClearLog();
	debugPrintf( __FUNCTION___T( "() START.\n" ) );

	if( 0 > hencInit() ) {
		MessageBox( GetDesktopWindow(), _T( "전자해도 커널을 초기화 할 수 없습니다." ), _T( "FATAL" ), MB_ICONERROR | MB_OK );
		return -1;
	}

	g_hInstance = hInstance;

	LoadString(hInstance, IDS_APP_TITLE, g_szTitle, MAX_LOADSTRING);
	LoadString(hInstance, IDC_SAMPLE, g_szWindowClass, MAX_LOADSTRING);
	MyRegisterClass(hInstance);

	if ( !InitInstance( hInstance, nCmdShow ) )
		return FALSE;

	loadMap();
	viewRedrawAll();

 	{
 		HWND hwnd = g_wndInfo.begin()->first;
		CWndMapView *mapview = &( g_wndInfo[ hwnd ].map( 0 ) );
		if( mapview ) {
			hencSelect( hwnd );
			mapview->setViewport();
			mapview->setMapParameter();

			double mx, my, mx2, my2;
			hencGetMapArea( mx, my, mx2, my2 );
			hencSetMapArea( mx, my, mx2, my2 );

			mapview->getMapParameter();
			InvalidateRect( hwnd, NULL, FALSE );
		}
 	}

	HACCEL hAccelTable = LoadAccelerators(hInstance, MAKEINTRESOURCE(IDC_SAMPLE));
	MSG msg;
	while ( GetMessage( &msg, NULL, 0, 0 ) ) {
		if ( TranslateAccelerator( msg.hwnd, hAccelTable, &msg ) )
			continue;

		TranslateMessage( &msg );
		DispatchMessage( &msg );
	}

	hencDeinit();

	debugPrintf( __FUNCTION___T( "() DONE.\n" ) );
	return (int) msg.wParam;
}

void arrangeViews( CWndInfo &info ) {
	//debugPrintf( __FUNCTION___T( "()\n" ) );
	RECT r;
	GetClientRect( info.m_hwnd, &r );

	int w = r.right;
	int h = r.bottom;

	int row = 2;
	int col = ( info.getViewCount() + row - 1 ) / row;

	if( info.getViewCount() == 1 ) {
		row = 1;
		col = 1;
	}
	if( info.getViewCount() == 2 ) {
		row = 1;
		col = 2;
	}

	for( int i = 0; i < info.getViewCount(); i++ ) {
		int wx = i % col * w / col;
		int wy = i / col * h / row;
		info.map( i ).m_viewx = wx;
		info.map( i ).m_viewy = wy;

		if( i == info.getViewCount() - 1 )
			info.map( i ).m_vieww = w - info.map( i ).m_viewx;
		else
			info.map( i ).m_vieww = w / col;
		info.map( i ).m_viewh = h / row;
	}
}

ATOM MyRegisterClass(HINSTANCE hInstance) {
	WNDCLASSEX wcex;

	wcex.cbSize = sizeof(WNDCLASSEX);

	wcex.style			= CS_HREDRAW | CS_VREDRAW | CS_DBLCLKS;
	wcex.lpfnWndProc	= WndProc;
	wcex.cbClsExtra		= 0;
	wcex.cbWndExtra		= 0;
	wcex.hInstance		= hInstance;
	wcex.hCursor		= LoadCursor(NULL, IDC_ARROW);
	wcex.hbrBackground	= (HBRUSH)(COLOR_WINDOW+1);
	wcex.lpszMenuName	= MAKEINTRESOURCE(IDC_SAMPLE);
	wcex.lpszClassName	= g_szWindowClass;

	wcex.hIconSm		= LoadIcon( wcex.hInstance, MAKEINTRESOURCE( IDI_SAMPLE ) );
	wcex.hIcon			= LoadIcon( hInstance, MAKEINTRESOURCE( IDI_SAMPLE ) );
	return RegisterClassEx(&wcex);
}

struct MONITOR_SELECT {
	int found;
	int m_monitorWant;
	RECT m_rectMonitor;
	HMONITOR m_monitor;
};

BOOL CALLBACK MonitorEnumProc( HMONITOR hMonitor, HDC hdcMonitor, LPRECT lprcMonitor, LPARAM dwData ) {
	MONITORINFO info;
	info.cbSize = sizeof( info );
	GetMonitorInfo( hMonitor, &info );
	int num = 0;
	if( info.dwFlags & MONITORINFOF_PRIMARY )
		num = 0;
	else
		num = 1;
	debugPrintf(
			_T( "MONITOR=%d, %dx%d~%dx%d, %s\n" ),
			hMonitor, lprcMonitor->left, lprcMonitor->top, lprcMonitor->right, lprcMonitor->bottom,
			num ? _T( "Secondary" ) : _T( "Primary" )
	);

	MONITOR_SELECT *ms = (MONITOR_SELECT *)dwData;
	if( num  == ms->m_monitorWant ) {
		ms->m_monitor = hMonitor;
		ms->m_rectMonitor = *lprcMonitor;
		ms->found = 1;
		return FALSE;
	}

	return TRUE;
}

BOOL InitInstance(HINSTANCE hInstance, int nCmdShow) {
	HWND hWnd;

	g_hInstance = hInstance; // 인스턴스 핸들을 전역 변수에 저장합니다.


	MONITOR_SELECT ms;
	ms.m_monitorWant = 0;
	ms.found = 0;

	debugPrintf( _T( "g_monitor %d\n" ), ms.m_monitorWant );
	EnumDisplayMonitors( NULL, NULL, MonitorEnumProc, (LPARAM )&ms );
	int x = 0, y = 0, w = 0, h = 0;
	if( ms.found ) {
		x = ms.m_rectMonitor.left;
		y = ms.m_rectMonitor.top;
		w = ms.m_rectMonitor.right - ms.m_rectMonitor.left;
		h = ms.m_rectMonitor.bottom - ms.m_rectMonitor.top - 40;
		debugPrintf( _T( "monitor %d at %d,%d size=%d,%d\n" ), ms.m_monitorWant, x, y, w, h );
	}

	{
		hWnd = CreateWindow(
				g_szWindowClass,
				g_szTitle,
				WS_OVERLAPPEDWINDOW,
				CW_USEDEFAULT, 0,
				CW_USEDEFAULT, 0,
				NULL,
				NULL,
				hInstance,
				NULL
		);

		if (!hWnd) {
			return FALSE;
		}

		ShowWindow(hWnd, nCmdShow);
		//ShowWindow(hWnd, SW_MAXIMIZE );
		UpdateWindow(hWnd);
	}

	return TRUE;
}

LRESULT onDestory(HWND hWnd, UINT message, WPARAM wParam, LPARAM lParam) {
	hencDelete( hWnd );

	if( --g_cntWindow <= 0 )
		PostQuitMessage(0);
	return  0;
}

void drawOnGL( HWND hwnd, CWndMapView *map ) {
	glEnable( GL_BLEND );
}

void drawOnDC( HWND hwnd, CWndMapView *map, HDC hdc ) {
	int x = map->m_viewx;
	int y = map->m_viewy;
	int w = map->m_vieww;
	int h = map->m_viewh;

	{
		CAutoSelectObject brush( hdc, GetStockObject( NULL_BRUSH ) );
		CAutoSelectObject pen( hdc, CreatePen( PS_SOLID, 1, RGB(0,0,0) ) );
		Rectangle( hdc, x, y, x + w, y + h );
	}
}

void drawOnDCAll( HWND hWnd, HDC hdc ) {
	CWndInfo &info = g_wndInfo[ hWnd ];

	for( int i = 0; i < info.getViewCount(); i++ ) {
		CWndMapView &map = info.map( i );
		map.setViewport();
		map.setMapParameter();
		drawOnDC( hWnd, &map, hdc );
	}
}

int glMapToDCAll( HWND hWnd, HDC hdc ) {
	RECT r;
	GetClientRect( hWnd, &r );
	int w = r.right;
	int h = r.bottom;

	{
		CAutoDeleteGdiObject bitmap( hencCaptureBuffer() );
 		{
 			CAutoBitmapDC hdcMem( bitmap );
			drawOnDCAll( hWnd, hdcMem );
			BitBlt( hdc, 0, 0, w, h, hdcMem, 0, 0, SRCCOPY );
		}
	}
//*////////////////////////////////////////////////////////////////////////////////////////////////

	return 0;
}

int glMapToDC( HWND hWnd, HDC hdc, CWndMapView *map  ) {
	RECT r;
	GetClientRect( hWnd, &r );
	int w = r.right;
	int h = r.bottom;

	{
		CAutoDeleteGdiObject bitmap( hencCaptureBuffer() );
 		{
 			CAutoBitmapDC hdcMem( bitmap );
			drawOnDC( hWnd, map, hdcMem );
			BitBlt(
					hdc,
					map->m_viewx,
					map->m_viewy,
					map->m_vieww,
					map->m_viewh,
					hdcMem,
					map->m_viewx,
					map->m_viewy,
					SRCCOPY
			);
		}
	}
//*////////////////////////////////////////////////////////////////////////////////////////////////

	return 0;
}

int updateViewport( HWND hWnd, CWndMapView *map ) {
	PFMCOUNTER_START( p );

	hencSelect( hWnd );
	map->setViewport();
	map->setMapParameter();
	hencClearViewport();
	hencRender();

	//debugPrintf( __FUNCTION___T( "() Viewport=%d,%d ~ %d, %d\n" ), map->m_viewx, map->m_viewy, map->m_vieww, map->m_viewh );
	drawOnGL( hWnd, map );
	HDC hdc = GetDC( hWnd );
	glMapToDC( hWnd, hdc, map );

	ReleaseDC( hWnd, hdc );
	return 0;
}

LRESULT onPaint( HWND hWnd, UINT message, WPARAM wParam, LPARAM lParam ) {
	PFMCOUNTER_START( p );

	PAINTSTRUCT ps;
	CWndInfo &info = g_wndInfo[ hWnd ];

	HDC hdc = BeginPaint(hWnd, &ps);
	//debugPrintf( _T( " hWnd=0x%x, HDC=0x%X\n" ), hWnd, hdc );

	RECT r;
	GetClientRect( hWnd, &r );
	int w = r.right;
	int h = r.bottom;

	hencSelect( hWnd );

	hencClearBackground();

	for( int i = 0; i < info.getViewCount(); i++ ) {
		CWndMapView &map = info.map( i );
		map.setViewport();
		map.setMapParameter();
		PFMCOUNTER_START( r );
		hencRender();
		debugPrintf( _T( "hencRender()=%.3f\n" ), PFMCOUNTER_DIFF( r ) );
		drawOnGL( hWnd, &map );
	}

	glMapToDCAll( hWnd, hdc );

	EndPaint(hWnd, &ps);
	return 0;
}

LRESULT onCommand(HWND hWnd, UINT message, WPARAM wParam, LPARAM lParam) {
	int wmId    = LOWORD(wParam);
	int wmEvent = HIWORD(wParam);
	// 메뉴의 선택 영역을 구문 분석합니다.
	switch (wmId) {
		case IDM_MAKE_VIEW1 :
			g_wndInfo[ hWnd ].m_cntView = 1;
			arrangeViews( g_wndInfo[ hWnd ] );
			InvalidateRect( hWnd, NULL, FALSE );
			break;
		case IDM_MAKE_VIEW4 :
			g_wndInfo[ hWnd ].m_cntView = 4;
			arrangeViews( g_wndInfo[ hWnd ] );
			InvalidateRect( hWnd, NULL, FALSE );
			break;
		case IDM_MAXVIEW :
			g_wndInfo[ hWnd ].m_cntView = MAX_VIEW;
			arrangeViews( g_wndInfo[ hWnd ] );
			InvalidateRect( hWnd, NULL, FALSE );
			break;
		case IDM_MORE_VIEW :
			if( ++ g_wndInfo[ hWnd ].m_cntView >= MAX_VIEW )
				g_wndInfo[ hWnd ].m_cntView = MAX_VIEW;
			arrangeViews( g_wndInfo[ hWnd ] );
			InvalidateRect( hWnd, NULL, FALSE );
			break;
		case IDM_LESS_VIEW :
			if( -- g_wndInfo[ hWnd ].m_cntView < 1 )
				g_wndInfo[ hWnd ].m_cntView = 1;
			arrangeViews( g_wndInfo[ hWnd ] );
			InvalidateRect( hWnd, NULL, FALSE );
			break;
		case IDM_NEW_WINDOW :
			{
				HWND hWnd = CreateWindow(
						g_szWindowClass,
						g_szTitle,
						WS_OVERLAPPEDWINDOW,
						CW_USEDEFAULT,
						0,
						CW_USEDEFAULT,
						0,
						NULL,
						NULL,
						g_hInstance,
						NULL
				);
				ShowWindow(hWnd, SW_NORMAL );
				UpdateWindow(hWnd);
			}
			break;
		case IDM_ABOUT:
			dlgAbout( hWnd );
			break;
		case IDM_EXIT:
			DestroyWindow(hWnd);
			break;
		default:
			return DefWindowProc(hWnd, message, wParam, lParam);
	}

	return 0;
}

LRESULT onSize(HWND hWnd, UINT message, WPARAM wParam, LPARAM lParam) {
	arrangeViews( g_wndInfo[ hWnd ] );
	return  0;
}

LRESULT onCreate(HWND hWnd, UINT message, WPARAM wParam, LPARAM lParam) {
	TCHAR buf[256];

	LPCREATESTRUCT cs = (LPCREATESTRUCT )lParam;

	g_cntWindow++;
	CWndInfo &info = g_wndInfo[ hWnd ];
	info.m_hwnd = hWnd;
	info.m_cntView = CNT_INITIAL_VIEW;
	_stprintf_s( buf, 256, _T( "HNS ENC 0x%X" ), hWnd );
	SetWindowText( hWnd, buf );
	HDC hdc = GetDC( hWnd );
	hencCreate( hWnd );

	{
		CWndMapView &map = info.map( 0 );

		map.m_centerx = 0;
		map.m_centery = 0;
		map.m_zoom = 1;
		map.m_degree = 0;
	}

	{
		CWndMapView &map = info.map( 1 );

		map.m_centerx = 126.411160;
		map.m_centery = 37.369847;
		map.m_zoom = 467.774752;
		map.m_degree = 0;
	}

	{
		CWndMapView &map = info.map( 2 );

		map.m_centerx = 129.023807;
		map.m_centery = 35.091644;
		map.m_zoom = 7081.278033;
		map.m_degree = 0;
	}

	{
		CWndMapView &map = info.map( 3 );

		map.m_centerx = 129.217037;
		map.m_centery = 35.164078;
		map.m_zoom = 753.355915;
		map.m_degree = 0;
	}

	return 0;
}

CWndMapView *getMapViewOfPos( CWndInfo &info, int x, int y ) {
	for( int i = 0; i < info.getViewCount(); i++ ) {
		CWndMapView &map = info.map( i );

		if( x >= map.m_viewx )
		if( x < map.m_viewx + map.m_vieww )
		if( y >= map.m_viewy )
		if( y < map.m_viewy + map.m_viewh )
			return &map ;
    }

	return NULL;
}

LRESULT CALLBACK onMbuttonDown(HWND hWnd, UINT message, WPARAM wParam, LPARAM lParam) {
	int x = GET_X_LPARAM( lParam );
	int y = GET_Y_LPARAM( lParam );
	//debugPrintf( _T( "WM_MBUTTONDOWN %d,%d\n" ), x, y );

	CWndInfo &info = g_wndInfo[ hWnd ];

	info.m_startx = info.m_mousex = x;
	info.m_starty = info.m_mousey = y;

	CWndMapView *mapview = getMapViewOfPos( info, x, y );
	if( mapview == NULL )
		return 0;

	hencSelect( hWnd );
	mapview->setViewport();
	mapview->setMapParameter();

	double dx = x;
	double dy = y;
	hencWinPosToMapPos( dx, dy );

	updateViewport( hWnd, mapview );
	return 0;
}

LRESULT CALLBACK onLbuttonDown(HWND hWnd, UINT message, WPARAM wParam, LPARAM lParam) {
	int x = GET_X_LPARAM( lParam );
	int y = GET_Y_LPARAM( lParam );
	//debugPrintf( _T( "WM_LBUTTONDOWN %d,%d\n" ), x, y );

	CWndInfo &info = g_wndInfo[ hWnd ];

	info.m_startx = info.m_mousex = x;
	info.m_starty = info.m_mousey = y;

	{
		CWndMapView *mapview = getMapViewOfPos( info, x, y );
		if( mapview == NULL )
			return 0;

		//debugPrintf( __FUNCTION___T( "() Viewport=%d,%d ~ %d, %d\n" ), mapview->m_viewx, mapview->m_viewy, mapview->m_vieww, mapview->m_viewh );
		hencSelect( hWnd );
		mapview->setViewport();
		mapview->setMapParameter();

		double dx = x;
		double dy = y;
		hencWinPosToMapPos( dx, dy );
		//debugPrintf( _T( "WinPos %d,%d -> MapPos %f,%f\n" ), x, y, dx, dy );
		mapview->m_centerx = dx;
		mapview->m_centery = dy;

		updateViewport( hWnd, mapview );
	}
	return 0;
}

LRESULT CALLBACK onLbuttonUp(HWND hWnd, UINT message, WPARAM wParam, LPARAM lParam) {
	int x = GET_X_LPARAM( lParam );
	int y = GET_Y_LPARAM( lParam );
	//debugPrintf( _T( "WM_LBUTTONUP %d,%d\n" ), x, y );
	CWndInfo &info = g_wndInfo[ hWnd ];
	info.m_mousex = x;
	info.m_mousey = y;

	return 0;
}

LRESULT CALLBACK onRbuttonDown(HWND hWnd, UINT message, WPARAM wParam, LPARAM lParam) {
	int x = GET_X_LPARAM( lParam );
	int y = GET_Y_LPARAM( lParam );
	//debugPrintf( _T( "WM_RBUTTONDOWN %d,%d\n" ), x, y );

	CWndInfo &info = g_wndInfo[ hWnd ];

	info.m_startx = info.m_mousex = x;
	info.m_starty = info.m_mousey = y;

	return 0;
}

LRESULT CALLBACK onLbuttonDoubleClick(HWND hWnd, UINT message, WPARAM wParam, LPARAM lParam) {
	int x = GET_X_LPARAM( lParam );
	int y = GET_Y_LPARAM( lParam );
	//debugPrintf( _T( "WM_LBUTTONDBLCLK %d,%d\n" ), x, y );
	CWndInfo &info = g_wndInfo[ hWnd ];
	info.m_mousex = x;
	info.m_mousey = y;

	CWndMapView *mapview = getMapViewOfPos( info, x, y );
	if( mapview ) {
		hencSelect( hWnd );
		mapview->setViewport();
		mapview->setMapParameter();

		double mx, my, mx2, my2;
		hencGetMapArea( mx, my, mx2, my2 );
		hencSetMapArea( mx, my, mx2, my2 );

		mapview->getMapParameter();
		updateViewport( hWnd, mapview );
		//InvalidateRect( hWnd, NULL, FALSE );
	}

	return 0;
}

LRESULT CALLBACK onRbuttonUp(HWND hWnd, UINT message, WPARAM wParam, LPARAM lParam) {
	int x = GET_X_LPARAM( lParam );
	int y = GET_Y_LPARAM( lParam );
	//debugPrintf( _T( "WM_RBUTTONUP %d,%d\n" ), x, y );
	CWndInfo &info = g_wndInfo[ hWnd ];
	info.m_mousex = x;
	info.m_mousey = y;

	CWndMapView *mapview = getMapViewOfPos( info, x, y );

	if( mapview == NULL )
		return 0;

 	if( x == info.m_startx )
 	if( y == info.m_starty ) {
 		//debugPrintf( _T( "--> Click Right\n" ) );
	}

	return 0;
}

LRESULT CALLBACK onRbuttonDoubleClick(HWND hWnd, UINT message, WPARAM wParam, LPARAM lParam) {
	int x = GET_X_LPARAM( lParam );
	int y = GET_Y_LPARAM( lParam );
	//debugPrintf( _T( "WM_RBUTTONDBLCLK %d,%d\n" ), x, y );
	CWndInfo &info = g_wndInfo[ hWnd ];
	info.m_mousex = x;
	info.m_mousey = y;

	CWndMapView *mapview = getMapViewOfPos( info, x, y );
	if( mapview ) {
		hencSelect( hWnd );
		mapview->setViewport();
		mapview->setMapParameter();
		mapview->m_degree = 0;
		updateViewport( hWnd, mapview );
		//InvalidateRect( hWnd, NULL, FALSE );
	}

	return 0;
}

LRESULT CALLBACK onMouseWheel(HWND hWnd, UINT message, WPARAM wParam, LPARAM lParam) {
	int x = GET_X_LPARAM( lParam );
	int y = GET_Y_LPARAM( lParam );
	int zDelta = GET_WHEEL_DELTA_WPARAM(wParam);
	POINT pt = { x, y };
	ScreenToClient( hWnd, &pt );
	x = pt.x;
	y = pt.y;

	//debugPrintf( _T( "WM_MOUSEWHEEL %d,%d delta=%d\n" ), x, y, zDelta );
	CWndInfo &info = g_wndInfo[ hWnd ];
	info.m_mousex = x;
	info.m_mousey = y;

	CWndMapView *mapview = getMapViewOfPos( info, x, y );

	if( mapview ) {
		if( wParam & MK_MBUTTON ) {
			if( zDelta > 0 )
				mapview->m_degree += 5;
			else
				mapview->m_degree -= 5;
		}
		else {
			if( zDelta > 0 )
				mapview->m_zoom *= 2.0;
			else
				mapview->m_zoom /= 2.0;
		}
		updateViewport( hWnd, mapview );
		//InvalidateRect( hWnd, NULL, FALSE );
	}

	return 0;
}

LRESULT CALLBACK onMouseMove(HWND hWnd, UINT message, WPARAM wParam, LPARAM lParam) {
	int x = GET_X_LPARAM( lParam );
	int y = GET_Y_LPARAM( lParam );

	CWndInfo &info = g_wndInfo[ hWnd ];
	int deltax = info.m_mousex - x;

	info.m_mousex = x;
	info.m_mousey = y;

	CWndMapView *mapview = getMapViewOfPos( info, x, y );

	if( mapview ) {
		hencSelect( hWnd );
		mapview->setViewport();
		mapview->setMapParameter();

		double dx = x;
		double dy = y;
		hencWinPosToMapPos( dx, dy );

		if( MK_RBUTTON & wParam ) {
			mapview->m_degree -= deltax;
			updateViewport( hWnd, mapview );
			//InvalidateRect( hWnd, NULL, FALSE );
		}
	}

	return 0;
}

LRESULT CALLBACK onKeyDown(HWND hWnd, UINT message, WPARAM wParam, LPARAM lParam) {
	return 0;
}

LRESULT CALLBACK onTimer(HWND hWnd, UINT message, WPARAM wParam, LPARAM lParam) {
	InvalidateRect( hWnd, NULL, FALSE );
	return 0;
}


LRESULT CALLBACK WndProc(HWND hWnd, UINT message, WPARAM wParam, LPARAM lParam) {
	switch (message) {
		case WM_TIMER :
			return onTimer( hWnd, message, wParam, lParam );
		case WM_KEYDOWN :
			return onKeyDown( hWnd, message, wParam, lParam );
		case WM_MBUTTONDOWN :
			return onMbuttonDown( hWnd, message, wParam, lParam );
		case WM_LBUTTONDOWN :
			return onLbuttonDown( hWnd, message, wParam, lParam );
		case WM_LBUTTONUP :
			return onLbuttonUp( hWnd, message, wParam, lParam );
		case WM_LBUTTONDBLCLK :
			return onLbuttonDoubleClick( hWnd, message, wParam, lParam );
		case WM_RBUTTONDOWN :
			return onRbuttonDown( hWnd, message, wParam, lParam );
		case WM_RBUTTONUP :
			return onRbuttonUp( hWnd, message, wParam, lParam );
		case WM_RBUTTONDBLCLK :
			return onRbuttonDoubleClick( hWnd, message, wParam, lParam );
		case WM_MOUSEWHEEL :
			return onMouseWheel( hWnd, message, wParam, lParam );
		case WM_MOUSEMOVE :
			return onMouseMove( hWnd, message, wParam, lParam );
		case WM_SIZE :
			return onSize( hWnd, message, wParam, lParam );
		case WM_CREATE :
			return onCreate( hWnd, message, wParam, lParam );
		case WM_COMMAND:
			return onCommand( hWnd, message, wParam, lParam );
		case WM_PAINT:
			return onPaint( hWnd, message, wParam, lParam );
		case WM_ERASEBKGND :
			break;
		case WM_DESTROY:
			return onDestory( hWnd, message, wParam, lParam );
		default:
			return DefWindowProc(hWnd, message, wParam, lParam);
	}
	return 0;
}