/***============================================================================
Questo programma serve unicamente per testare l'impiego della classe OROLOGIO.
Dal momento che, a titolo puramente esemplificativo, usa un semplice timer
gestito tramite callback, la precisione dell'ora indicata NON e' assicurata.
=============================================================================**/
#include <windows.h>
#include <gdiplus.h>
using namespace Gdiplus;
#include "orologio.h"
// === COSTANTI ================================================================
const char kStrNomeClasse[] = "ClasseTestOrogologio";
const char kStrNomeProgrm[] = "Test classe orologio";
const int kWFinestra = 800;
const int kHFinestra = 450;
const int kMargine = 30;
const unsigned int kIdTimer = 1000;
// === VARIABILI GLOBALI =======================================================
OROLOGIO *gOPtr1 = NULL; // alias globali degli oggetti locali OROLOGIO
OROLOGIO *gOPtr2 = NULL; // definiti in WinMain()
// === PROTOTIPI DI FUNZIONI ===================================================
LRESULT CALLBACK WndProc( HWND hWnd, UINT msg, WPARAM wPar, LPARAM lPar );
VOID CALLBACK TimerProc( HWND hwnd, UINT uMsg, UINT idEvent, DWORD dwTime );
void ModificaSecondoOrologio( void );
bool RegistraClasse( HINSTANCE hInst );
bool CreaFinestra( HWND *hwnd, HINSTANCE hInst );
bool InizializzaGdiPlus( void );
void DismettiGdiPlus( void );
void Errore( int codice, HWND hwnd = NULL );
void Errore( const char *msg, HWND hwnd = NULL );
/*==============================================================================
Funzione d'accesso al programma
==============================================================================*/
int WINAPI WinMain(
HINSTANCE hInst, HINSTANCE hPrevInst, LPSTR cmdLine, int nCmdShow ) {
HWND hwnd = NULL;
MSG msg = {0};
if( !RegistraClasse(hInst) ) { Errore(1); return 0; }
if( !CreaFinestra(&hwnd,hInst) ) { Errore(2); return 0; }
if( !InizializzaGdiPlus() ) { Errore(3); return 0; }
ShowWindow( hwnd, nCmdShow );
try {
Rect ro( kMargine, kMargine,
kWFinestra/2-1.5*kMargine,
kHFinestra-2*kMargine );
OROLOGIO o1( hwnd, &ro );
ro.X += kWFinestra/2-0.5*kMargine; // sposta il Rect
OROLOGIO o2( hwnd, &ro );
gOPtr1 = &o1; // definisce un alias globale dell'oggetto
gOPtr2 = &o2; // definisce un alias globale dell'oggetto
// a titolo di prova, modifica alcune
// caratteristiche del secondo orologio
ModificaSecondoOrologio();
TimerProc( NULL, 0, kIdTimer, 0x00000000 );
while( GetMessage( &msg, NULL, 0, 0 ) > 0 ) {
TranslateMessage( &msg );
DispatchMessage( &msg );
}
gOPtr1 = NULL;
gOPtr2 = NULL;
} catch ( const char *strErr ) {
Errore( strErr, hwnd );
} catch( ... ) {
Errore(4);
}
DismettiGdiPlus();
return 0;
}
/*==============================================================================
Window Procedure per la finestra principale del programma. Tra le altre cose...
1. imposta il timer che chiama TimerProc una ventina di volte al secondo per
ridisegnare l'orologio in base all'ora corrente
2. richiede agli orologi di trasferire il proprio offscreen a schermo ogni
qualvolta si riceva un messaggio di tipo WM_PAINT
==============================================================================*/
LRESULT CALLBACK WndProc( HWND hwnd, UINT msg, WPARAM wPar, LPARAM lPar ) {
switch( msg ) {
case WM_CREATE:
// il sistema chiama TimerProc +/- 20 volte al sec
SetTimer( hwnd, kIdTimer, 50, TimerProc );
break;
case WM_PAINT: {
PAINTSTRUCT ps;
BeginPaint( hwnd, &ps );
// aggiorna le immagini già negli offscreen,
// senza ridisegnare gli orologi da zero
if( gOPtr1 ) gOPtr1->Aggiorna();
if( gOPtr2 ) gOPtr2->Aggiorna();
EndPaint( hwnd, &ps );
} break;
case WM_CLOSE:
KillTimer( hwnd, kIdTimer );
PostQuitMessage( 0 );
break;
default:
return DefWindowProc( hwnd, msg, wPar, lPar );
}
return 0;
}
/*==============================================================================
Chiamata dal sistema ad ogni scadenza del timer kIdTimer (approssimativamente
20 volte al secondo) e verifica l'ora locale. Se l'ora di sistema e' cambiata
rispetto all'ultima verifica aggiorna i due oggetti di classe OROLOGIO affinche'
mostrino rispettivamente l'ora locale e quella UTC.
==============================================================================*/
VOID CALLBACK TimerProc( HWND hwnd, UINT uMsg, UINT idEvent, DWORD dwTime ) {
if( gOPtr1 == NULL || gOPtr2 == NULL ) return;
SYSTEMTIME st, lt; // [s]ystem [t]ime (UTC) e [l]ocal [t]ime
GetLocalTime( < ); // ora locale
// solo se l'ora e' effettivamente cambiata...
if( gOPtr1->RicavaSecondo() != lt.wSecond ) {
GetLocalTime( < ); // ora locale, di nuovo
GetSystemTime( &st ); // ora UTC
gOPtr1->ImpostaOra( lt.wHour, lt.wMinute, lt.wSecond );
gOPtr2->ImpostaOra( st.wHour, st.wMinute, st.wSecond );
gOPtr1->Traccia( false ); // "false" per evitare che il primo orologio
gOPtr2->Traccia( false ); // venga riportato sullo schermo fino al
// momento in cui anche il secondo orologio
// e' pronto in offscreen
gOPtr1->Aggiorna(); // riporta sullo schermo entrambi gli orologi col
gOPtr2->Aggiorna(); // minimo intervallo di tempo possibile tra i due
}
}
/*==============================================================================
A titolo di esempio, modifica alcune delle caratteristiche del secondo orologio.
==============================================================================*/
void ModificaSecondoOrologio( void ) {
if( gOPtr2 ) {
gOPtr2->ImpostaColoreSfondoOggetto( 0x33EEEEEE );
gOPtr2->SpessoreBordoQuadrante( 2.0f*gOPtr2->SpessoreBordoQuadrante() );
gOPtr2->cSfondoQuadrante = 0xEEFFCC00;
gOPtr2->cBordoQuadrante = 0xCC006699;
gOPtr2->cNumeriQuadrante = gOPtr2->cBordoQuadrante;
gOPtr2->c[kLancettaOre] = gOPtr2->cBordoQuadrante;
gOPtr2->s[kLancettaOre] *= 2.0f;
gOPtr2->l[kLancettaOre] *= 0.75f;
gOPtr2->c[kLancettaMinuti] = gOPtr2->cBordoQuadrante;
gOPtr2->s[kLancettaMinuti] *= 1.75f;
gOPtr2->l[kLancettaMinuti] *= 0.825f;
gOPtr2->c[kLancettaSecondi] = gOPtr2->cBordoQuadrante;
gOPtr2->s[kLancettaSecondi] *= 2.0f;
gOPtr2->l[kLancettaSecondi] *= 0.85f;
gOPtr2->c[kMarcatoriOre] = gOPtr2->cBordoQuadrante;
gOPtr2->s[kMarcatoriOre] *= 2.0f;
gOPtr2->c[kMarcatoriMinuti] = gOPtr2->cBordoQuadrante;
gOPtr2->s[kMarcatoriMinuti] *= 2.0f;
}
}
/*==============================================================================
Registra la classe per la finestra principale del programma.
==============================================================================*/
bool RegistraClasse( HINSTANCE hInst ) {
WNDCLASS wc = {0};
wc.lpfnWndProc = WndProc;
wc.hInstance = hInst;
wc.hCursor = LoadCursor( NULL, IDC_ARROW );
wc.hbrBackground = (HBRUSH)GetStockObject( WHITE_BRUSH );
wc.lpszClassName = kStrNomeClasse;
return( RegisterClass(&wc) != 0 );
}
/*==============================================================================
Crea la finestra principale del programma, centrandola sullo schermo.
==============================================================================*/
bool CreaFinestra( HWND *hwnd, HINSTANCE hInst ){
DWORD stile = WS_POPUP | WS_CAPTION | WS_SYSMENU | WS_VISIBLE;
RECT r = { 0, 0, kWFinestra, kHFinestra };
AdjustWindowRect( &r, stile, FALSE );
OffsetRect( &r,
-r.left+(GetSystemMetrics(SM_CXSCREEN)-(r.right-r.left))/2,
-r.top+(GetSystemMetrics(SM_CYSCREEN)-(r.bottom-r.top))/2 );
*hwnd = CreateWindow( kStrNomeClasse, kStrNomeProgrm, stile,
r.left, r.top, r.right-r.left, r.bottom-r.top, 0, 0, hInst, NULL );
return *hwnd != NULL;
}
// === FUNZIONI PER L'INIZIALIZZAZIONE E LA DISMISSIONE DI GDI+ ================
static ULONG_PTR gdiplusToken = 0;
bool InizializzaGdiPlus( void ) {
GdiplusStartupInput gdiplusStartupInput;
if( GdiplusStartup( &gdiplusToken, &gdiplusStartupInput, NULL ) != Ok )
return false;
return true;
}
void DismettiGdiPlus( void ) {
GdiplusShutdown( gdiplusToken );
}
// === FUNZIONI PER LA NOTIFICA DEGLI ERRORI ===================================
void Errore( int codice, HWND hwnd ) {
static const char *strErr[] = {
"Classe non registrata. ",
"Finestra non creata. ",
"GDI+ non inizializzato. ",
"Memoria non allocata. "
};
if( codice > -1 && codice < 4 ) {
MessageBox( hwnd, strErr[codice], kStrNomeProgrm, MB_OK|MB_ICONERROR );
}
else {
char msg[48];
wsprintf( msg, "Si e' verificato un errore! Codice #%d", codice );
MessageBox( hwnd, msg, kStrNomeProgrm, MB_OK|MB_ICONERROR );
}
}
void Errore( const char *msg, HWND hwnd ) {
MessageBox( hwnd, msg, kStrNomeProgrm, MB_OK|MB_ICONERROR );
}