/**************/ /* nappula3.c */ /****************************************************************************/ /* ** Aliohjelmakirjasto nappuloiden ja laskureiden piirtämiseksi näytölle. ** Windows-versio ilman nappuloiden siirtomahdollisuutta. ** ** Vesa Lappalainen 15.8.1992 ** ** Kutsuvassa ohjelmassa määritellään taulukko näppäimistä ja ** taulukko laskureista. ** ** Näppäimen painamista joko näppäimistöltä tai hiireltä odotetaan ** aliohjelmalla 'lue_komento'. Aliohjelma palauttaa valittua ** komentoa vastaavan tunnuksen. Tunnukset on määritelty ** käyttäjän ohjelmassa. Negatiiviset tunnukset on varattu ** systeemikäyttöön ja käyttäjän ohjelman on reagoitava myös niihin. ** Esim. mikäli palautetaan komento PIIRRA, pitää käyttäjän piirtää ** koko näyttö uudelleen (koska joku on sen sotkenut). ** /****************************************************************************/ #include #include #include #include "nappula3.h" /****************************************************************************/ #define skaalaa_x(x) ((int)(((long int)x - MIN_X) * KERROIN_X + MIN_H_X)) #define skaalaa_y(y) ((int)(((long int)y - MIN_Y) * KERROIN_Y + MIN_H_Y)) #define RECT_TO_4(r,dr) r.left-dr,r.top-dr,r.right+dr,r.bottom+dr #define MainWClass "LaskinLuokka" static nappain_tyyppi *nappaimet = NULL; static laskuri_tyyppi *laskurit = NULL; static HWND hWnd; static MSG msg; static int KERROIN_X = ( MAX_H_X - MIN_H_X + 1) / ( MAX_X - MIN_X +1 ); static int KERROIN_Y = ( MAX_H_Y - MIN_H_Y + 1) / ( MAX_Y - MIN_Y +1 ); static int MIN_LEVEYS = 0; static int lisaviesti = 0; static int paaviesti = EI_KOMENTOA; /* Värien määrittelyt: */ #define BLACK RGB(0,0,0) #define BLUE RGB(0,0,255) #define CYAN RGB(0,255,255) #define YELLOW RGB(255,255,0) #define GRAY RGB(180,180,180) /****************************************************************************/ int /* 0 koord. ei laatikossa */ laatikossa( /* 1 koord on laatikossa */ laatikko_tyyppi *tila, /* Tutkittava laatikko */ POINT *pt /* Tutkittava koodrinaatti */ ) /* Onko piste laatikon sisällä vai ei? */ { if ( ( tila->vasen_yla.x > pt->x ) || ( tila->vasen_yla.y > pt->y ) || ( tila->vasen_yla.x + tila->koko.x <= pt->x ) || ( tila->vasen_yla.y + tila->koko.y <= pt->y ) ) return 0; return 1; } /****************************************************************************/ int /* -1 jollei hiiri minkään napin koh */ tutki_hiiri( /* napin numero, jonka kohdalla hiiri*/ nappain_tyyppi *nappaimet, /* Tutkittavat näppäimet */ LONG lparam /* Viestin parametri */ ) /* ** Tutkitaan onko hiiri jonkin napin kohdalla. Mikäli on, laitetaan ** vastaavan napin viestit ja palautetaan napin numero. ** Muuten palautetaan aina EI_KOMENTOA. ----------------------------------------------------------------------------*/ { int i; POINT pt; pt = MAKEPOINT(lparam); for (i=0; nappaimet[i].valinta_kirjain; i++) { if ( laatikossa(&nappaimet[i].nappain,&pt) ) { paaviesti = nappaimet[i].komento; lisaviesti = nappaimet[i].lisa_viesti; return i; /* Palautetaan napin numero */ } } return EI_KOMENTOA; } /****************************************************************************/ int tutki_kirjaimet(nappain_tyyppi *nappaimet, WORD wParam) /* Tutkitaan onko painettu kirjain jonkin näppäimen valintakirjain. ** Mikäli on, laitetaan näppäimen viesti pääviestiksi. ** Palautetaan löytyneen napin numero. ----------------------------------------------------------------------------*/ { int i; char c = toupper(wParam); for (i=0; nappaimet[i].valinta_kirjain; i++) if ( nappaimet[i].valinta_kirjain == c ) { paaviesti = nappaimet[i].komento; lisaviesti = nappaimet[i].lisa_viesti; return i; } return EI_KOMENTOA; } /****************************************************************************/ int lue_komento( /* Valitun komennon tunnistin */ nappain_tyyppi *pnappaimet, /* Tutkittavat näppäimet */ laskuri_tyyppi *plaskurit, /* Käytettävät laskurit */ int *lisa /* Komennon lisäosa. */ ) /* ** Funktio palauttaa valitun komennon tunnuksen ja mahdollisen lisäviestin. ** Windows versiossa funktio on toteutettu siten, että ohjelman pääsilmukka ** on sijoitettu tähän. 'MainWndProc' huolehtii sitten Windowsin kutsumana ** viestien käsittelystä. Mikäli viestistä seuraa, että 'lue_komento'- ** aliohjelman pitäisi palauttaa jotakin kutsuvalle ohjelmalle, asetetaan ** globaali (static) muuttuja 'paaviesti' komentoa vastaavaan arvoon. ** 'lue_komento' odottaa tämän muuttujan muuttumista muuhun arvoon kuin ** EI_KOMENTOA ja palauttaa sitten tämän arvon. ** ** Toteutus ei ole kovin elegantti, mutta mikäli itse näppäimiä käyttävät ** ohjelmat halutaan säilyttää samanlaisina vastaavien DOS-ohjelmien kanssa, ** on ratkaisun oltava jotain tämänkaltaista. ----------------------------------------------------------------------------*/ { int viesti; nappaimet = pnappaimet; laskurit = plaskurit; paaviesti = EI_KOMENTOA; while ( paaviesti == EI_KOMENTOA ) { if (!GetMessage(&msg,NULL,NULL,NULL)) return SYS_EXIT; TranslateMessage(&msg); /* Tulkitaan virtuaaliset näp. koodit */ DispatchMessage(&msg); /* Lähetetään viesti ikkunalle */ } *lisa = lisaviesti; viesti = paaviesti; paaviesti = EI_KOMENTOA; return viesti; } /****************************************************************************/ void piirra_laatikko(laatikko_tyyppi *tila, char *st, COLORREF color) { HDC hDC; DWORD dw; RECT nelio; HBRUSH hbrOld; HBRUSH hbr; int x_lev,y_kork; if ( ( hDC = GetDC(hWnd) ) == NULL ) return; x_lev = tila->koko.x; y_kork = tila->koko.y; if ( !tila->muutettu ) { /* Muutetaan tilan tiedot laitekoordinaatteihin */ dw = GetTextExtent(hDC,st,lstrlen(st)); if ( x_lev == 0 ) { x_lev = max(LOWORD(dw),MIN_LEVEYS); tila->koko.x = x_lev; } else tila->koko.x = ( x_lev *= KERROIN_X ); if ( y_kork == 0 ) { y_kork = HIWORD(dw); tila->koko.y = y_kork; } else tila->koko.y = ( y_kork *= KERROIN_Y ); tila->vasen_yla.x = skaalaa_x(tila->vasen_yla.x); tila->vasen_yla.y = skaalaa_y(tila->vasen_yla.y); tila->muutettu = 1; } if ( ( x_lev < 0 ) || ( y_kork < 0 ) ) return; /*----------------------------------------------------------------*/ /* Maalataan laatikon sisus taustavärillä: */ nelio.left = tila->vasen_yla.x; nelio.top = tila->vasen_yla.y; nelio.right = tila->vasen_yla.x+x_lev; nelio.bottom = tila->vasen_yla.y+y_kork; hbr = CreateSolidBrush(color); hbrOld = SelectObject(hDC, hbr); Rectangle(hDC,RECT_TO_4(nelio,4)); Rectangle(hDC,RECT_TO_4(nelio,2)); SelectObject(hDC,hbrOld); DeleteObject(hbr); ReleaseDC(hWnd,hDC); } /****************************************************************************/ void tulosta_jono_laatikkoon( /* Tulostetaan jono laatikkoon. */ laatikko_tyyppi *tila, /* Laatikko johon tulostetaan */ char *st, /* Tulostettava merkkijono */ COLORREF tcolor, /* Tekstin väri */ COLORREF bcolor, /* Taustan väri */ UINT textalign /* Tekstin keskitys */ ) /* ** Tulostetaan jono laatikkoon. Jono keskitetään aina korkeussuunnassa, ** mutta leveyssuunnassa keskitykseen voidaan vaikuttaa textalign-param. ** Tunnetut kesitykset: ** TA_CENTER - keskitys ** TA_RIGHT - oikeaan reunaan ** muut - vasempaan reunaan ** ** Mikäli jono ei mahdu laatikkoon, menee se sen reunojen yli! ----------------------------------------------------------------------------*/ { HDC hDC; int x,y; x = tila->vasen_yla.x; if ( textalign == TA_CENTER ) x += tila->koko.x/2; if ( textalign == TA_RIGHT ) x += tila->koko.x; y = tila->vasen_yla.y + (tila->koko.y - KERROIN_Y)/2; if ( ( hDC = GetDC(hWnd) ) == NULL ) return; SetTextColor(hDC,tcolor); SetBkColor(hDC,bcolor); SetTextAlign(hDC, textalign); TextOut(hDC,x,y,st,lstrlen(st)); ReleaseDC(hWnd,hDC); } /****************************************************************************/ void nayta_laskuri(laskuri_tyyppi *laskuri) { char s[200]; sprintf(s,laskuri->format,laskuri->arvo); tulosta_jono_laatikkoon(&laskuri->tila,s,BLACK,CYAN,TA_RIGHT); } /****************************************************************************/ void piirra_nappula(nappain_tyyppi *nappula) { if ( paaviesti == PIIRRA ) return; piirra_laatikko(&nappula->nappain,nappula->teksti,YELLOW); tulosta_jono_laatikkoon(&nappula->nappain,nappula->teksti,BLACK,YELLOW,TA_CENTER); } /****************************************************************************/ void piirra_laskuri(laskuri_tyyppi *laskuri) { if ( paaviesti == PIIRRA ) return; piirra_laatikko(&laskuri->tila,"X",CYAN); nayta_laskuri(laskuri); } /****************************************************************************/ void nollaa_laskurit(laskuri_tyyppi *laskurit) { int i; for (i=0; laskurit[i].tunnus; i++) { laskurit[i].arvo=0; piirra_laskuri(&laskurit[i]); } } /****************************************************************************/ void lisaa_laskuria(laskuri_tyyppi *laskurit,int tunnus) /* Lisätään valitun laskurin arvoa yhdellä. */ { int i; for (i=0; laskurit[i].tunnus; i++) { /* Ets. laskuri */ if ( laskurit[i].tunnus == tunnus ) { laskurit[i].arvo++; nayta_laskuri(&laskurit[i]); return; } } } /****************************************************************************/ void alusta_nappulat(nappain_tyyppi *pnappulat) { nappaimet = pnappulat; } /****************************************************************************/ void init_globals(void) /* Alustetaan globaalit muuttujat: */ { HDC hDC; DWORD dw; if ( ( hDC = GetDC(hWnd) ) == NULL ) return; dw = GetTextExtent(hDC,"X",1); KERROIN_X = LOWORD(dw); KERROIN_Y = HIWORD(dw); dw = GetTextExtent(hDC,"M",1); MIN_LEVEYS = LOWORD(dw); ReleaseDC(hWnd,hDC); } /****************************************************************************/ void tyhjenna_ruutu(void) { /* UpdateWindow(hWnd); */ } /****************************************************************************/ LONG FAR PASCAL /* Palautetaan miten viesti käs. */ MainWndProc( /* Usein WIndowsin oletuskäsit. arvo */ HWND hWnd, /* Ikkunan kahva, jolle viesti tulee */ unsigned message, /* Viestin arvo. */ WPARAM wParam, /* Viestin lisäosa. */ LPARAM lParam /* Viestin lisäosa. */ ) /* ** Käsitellään Windowsilta tulevat viestit. Mikäli viesti aiheuttaa ** jonkin varsinaisen ohjelman komennon, laitetaan globaaliin muuttujaan ** 'paaviesti' tämän komennon arvo. Tällöin aliohjelmassa 'lue_komento' ** loppuu viestisilmukan käsittely ja komennon arvo palautuu varsinaiselle ** ohjelmalle. ----------------------------------------------------------------------------*/ { switch (message) { case WM_CHAR: /* Painettu kirjainnäppäintä: */ tutki_kirjaimet(nappaimet,wParam); break; case WM_LBUTTONDOWN: /* Hiiren vasen nappi alas: */ tutki_hiiri(nappaimet,lParam); break; case WM_PAINT: /* Viesti: Piirrä ikkuna uudelleen */ paaviesti = PIIRRA; break; case WM_DESTROY: /* Viesti: ikkuna hävitetään */ PostQuitMessage(0); return NULL; default: /* Antaa Windowsin käsitellä muut */ break; } return (DefWindowProc(hWnd, message, wParam, lParam)); } /****************************************************************************/ int PASCAL /* NULL-jos ohjelmaa ei saada käynt. */ WinMain( /* Muuten ohjelman onnist. kuv. arvo */ HINSTANCE hInstance, /* Esiintymän kahva */ HINSTANCE hPrevInstance, /* Edellisen esiintymän kahva. */ LPSTR lpCmdLine, /* Kutsun komentorivi. */ int nCmdShow /* Miten ikkuna näytetään? */ ) /* ** Tämä on Windows-pääohjelma, joka alustaa ikkunaluokan ja ikkunan. ** Alustamisen jälkeen pitäisi pyörittää viestisilmukkaa, mutta koska ** aliohjelmakirjsto on tehty DOS-yhteensopivaksi, on viestisilmukan ** pyörittäminen jätetty aliohjelman 'lue_komento'-huoleksi. ** Käyttäjän pääohjelman, jonka nimen täytyy aina olla 'laskin_main', ** pitää pitää huoli siitä, että aliohjelmaa 'lue_komento' kutsutaan ** riittävän usein, jotta muutkin Windows-ohjelmat saavat vuoronsa. ** ----------------------------------------------------------------------------*/ { #pragma argsused WNDCLASS wc; /* Ikkunaluokka */ if (!hPrevInstance) { /* Onko muita esiintymiä käynnisssä? */ wc.style = NULL; wc.lpfnWndProc = MainWndProc; wc.cbClsExtra = 0; wc.cbWndExtra = 0; wc.hInstance = hInstance; wc.hIcon = LoadIcon(NULL, IDI_APPLICATION); wc.hCursor = LoadCursor(NULL, IDC_ARROW); wc.hbrBackground = GetStockObject(WHITE_BRUSH); wc.lpszMenuName = NULL; wc.lpszClassName = MainWClass; if (!RegisterClass(&wc)) return (FALSE); } hWnd = CreateWindow(MainWClass,"Windows Laskuri 0.1",WS_OVERLAPPEDWINDOW, CW_USEDEFAULT,CW_USEDEFAULT,CW_USEDEFAULT,CW_USEDEFAULT, NULL,NULL,hInstance,NULL); if (!hWnd) return (FALSE); ShowWindow(hWnd, nCmdShow); /* Näytetään ikkuna */ init_globals(); laskin_main(); return (msg.wParam); /* Palautetaan PostQuitMessage-funktion arvo */ }