/**************/ /* nappulaw.c */ /****************************************************************************/ /* ** Aliohjelmakirjasto nappuloiden ja laskureiden piirtämiseksi näytölle. ** Windows-versio nappuloiden siirto ja suurennusmahdollisuudella. ** ** 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). ** ** Nappulat toteutettu Windowsin valmiilla palasilla */ /****************************************************************************/ #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 SISA_LISA 3 /* 2x raamin sisäreunan etäisyys vars. laatikosta */ #define LAATIKKO_TO_4k(r,dr) r.vasen_yla.x-2*dr,r.vasen_yla.y-dr,\ r.koko.x+4*dr,r.koko.y+2*dr #define MainWClass "LaskinLuokka" static nappain_tyyppi *nappaimet = NULL; static laskuri_tyyppi *laskurit = NULL; static HANDLE hInstance; 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; #define TUNNUS_ORG 1000 static int g_tunnus = 0; /* 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 tutki_nappulat(nappain_tyyppi *nappaimet, WORD wParam) /* Tutkitaan onko jollakin nappulalla WM_COMMANDin palauttama wtunnus. ** Mikäli on, laitetaan näppäimen viesti pääviestiksi. ** Palautetaan löytyneen napin numero. ----------------------------------------------------------------------------*/ { int i; for (i=0; nappaimet[i].valinta_kirjain; i++) if ( nappaimet[i].nappain.wtunnus + TUNNUS_ORG == wParam ) { paaviesti = nappaimet[i].komento; lisaviesti = nappaimet[i].lisa_viesti; return i; } 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 = (char)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 maarita_koko( /* Määritetään laatikon koko */ laatikko_tyyppi *tila, /* Laatikon koordinaatit */ char *st /* Jos laatikolla ei ole kokoa, ks.st*/ ) /* ** Laatikkotyypissä on kenttä 'muutettu'. Mikäli tämä kenttä on tosi, on ** laatikon koordinaatit laitekoordenaateissa. Muuten laatikon koordin. ** ovat X-kirjaimen kokoina ilmoitettu. Aluksi tehdään koon muutos ** laitekoordinaatteihin. Jos laatikon kokoa ei ole annettu, laitetaan ** laatikon korkeudeksi parametrina tulevan tekstin korkeus ja leveydeksi ** parametrina tulevan teksin leveys. Leveyttä ei kuitenkaan laiteta ** M-kirjaimen leveyttä pienemmäksi. ** Jos laatikon koko-koordinaateista jompi kumpi on <0, ei laatikkoa ** piirretä näkyviin. ** ----------------------------------------------------------------------------*/ { HDC hDC; DWORD dw; RECT nelio; 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((int)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; } ReleaseDC(hWnd,hDC); } /****************************************************************************/ void nayta_laskuri(laskuri_tyyppi *laskuri) { char s[200]; sprintf(s,laskuri->format,laskuri->arvo); SetWindowText(laskuri->tila.hwnd,s); /* BLACK,CYAN,TA_RIGHT */ } /****************************************************************************/ void piirra_nappula(nappain_tyyppi *nappula) { if ( nappula->nappain.hwnd ) return; /* Piirtyy automaattisesti */ maarita_koko(&nappula->nappain,nappula->teksti); nappula->nappain.wtunnus = ++g_tunnus; nappula->nappain.hwnd = CreateWindow("Button",nappula->teksti, WS_BORDER | WS_CHILD | WS_VISIBLE, LAATIKKO_TO_4k(nappula->nappain,SISA_LISA), hWnd,(HMENU)(g_tunnus+TUNNUS_ORG),hInstance,NULL); } /****************************************************************************/ void piirra_laskuri(laskuri_tyyppi *laskuri) { if ( !laskuri->tila.hwnd ) { /* Piirtyy automaattisesti */ maarita_koko(&laskuri->tila,"X"); /* CYAN */ laskuri->tila.wtunnus = ++g_tunnus; laskuri->tila.hwnd = CreateWindow("Static","", WS_BORDER | WS_CHILD | WS_VISIBLE | SS_RIGHT, LAATIKKO_TO_4k(laskuri->tila,SISA_LISA), hWnd,(HMENU)(g_tunnus+TUNNUS_ORG),hInstance,NULL); } 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); return NULL; case WM_COMMAND: tutki_nappulat(nappaimet,wParam); /* SetFocus(hWnd); /* Muuten jäädään nappulaan ja näppäimistö ei ole*/ return NULL; /* aktiivinen! */ 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_l, /* 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. ** ----------------------------------------------------------------------------*/ { WNDCLASS wc; /* Ikkunaluokka */ if (lpCmdLine); /* Hämäystä, jottei valitusta param. käytt. */ 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); } hInstance = hInstance_l; hWnd = CreateWindow(MainWClass,"Windows Laskuri 0.2",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 */ }