/**************/ /* savepos.c */ /***************************************************************************** PROGRAM: savepos.c PURPOSE: Tallettaa tai ottaa ikkunan paikan INI-tiedostosta AUTHOR: Vesa Lappalainen 27.10.1993 USAGE: Lukee ja kirjoittaa parametrina tuotavaan INI-tiedostoon ikkunan paikan. INI-tiedoston muoto: [Paikka] x=10 y=20 leveys=200 (vaikuttaa vain jos kutsun size != 0) korkeus=120 (vaikuttaa vain jos kutsun size != 0) minmax=1 1 = normaali, 2 = ikoni, 3 = maksimoitu Aliohjelmaa SavePos(HWND hWnd,LPCSTR inifilename,LPCSTR section,int size) kutsutaan jossakin ennen ikkunan ikkunan häviämistä, esim WM_DESTROY tai WM_CLOSE -viestissä. Aliohjelmaa RestorePos(HWND hWnd,LPCSTR inifilename,LPCSTR section,int size) kutsutaan esim. WM_CREATE tai WM_INITDIALOG -viestissä tai ikkunan luonnin jälkeen. Jos kyseessä Dialogi, niin WM_CREATE vaiheessa ei ole viisasta kutsua tätä, vaan pitää kutsua myöhemmin. Tosin jos dialogilla on luokka, niin WM_INITDIALOG-viestiä ei saada ikkunafunktioon, ja WM_CREATE on liian aikaisin. Tällöin täytyy tehdä dialogin funktio, jossa WM_INITDIALOG-käsitellään tai sitten esim. WM_SHOWWINDOW kutsuun lippu, jolla 1. kutsukerralla kutsutaan RestorePos. Parametrilla size määrätään välitetäänkö ikkunan koosta lainkaan! (Esim. dialogeille ei välttämättä kannata käyttää size==1, koska jos muutetaan WIndowsin asetuksia, niin dialogin koko muuttuu, mutta sitä ei voida hiirellä palauttaa oikeaan kokoonsa!). On myös vakiot SAVEPOS_SIZE (1) ja SAVEPOS_NOSIZE (0). Jos inifilaname == NULL, käytetään nimenä ohjelman nimeä+.INI ------------------------------------------------------------------------------ Samalla aliohjelma on malli seuraavista: 1. Miten sama tiedosto voi olla sekä "tavallisena"-aliohjelmana että DLL-kirjaston aliohjelmana (makro WINAPI_LIB hoitaa tämän) Tärkeää on tällöin muistaa välittää kaikki osoitteet pitkinä! Siis: ei char *, vaan LPSTR (tai LPCSTR) ei RECT *, vaan RECT FAR * Osoitteiden täytyy olla pitkiä, koska jos aliohjelma on DLL:ssä, on sille vietävät parametrit aivan eri data-segmentissä kuin itse kutsuva ohjelma. Tätä ongelmaahan ei ole jos tehdään "tavallinen" linkitys. 2. Miten makrojen avulla voidaan tehdä eri tilanteissa eri tavalla toimivia "aliohjelmia". Makro INI_WNDL joko lukee tai kirjoittaa INI-tiedostoa sen mukaan millainen makro INI sattuu olemaan makron INI_WNDL kutsuhetkellä. Tämä tilanne tulee usein vastaan nimenomaan tiedoston lukemisen ja kirjoittamisen yhteydessä, jolloin koodi on itse lukemista tai kirjoittamista lukuunottamatta täysin samanlainen. Tällaisella makrolla (tai sopivalla aliohjelmalla) saadaan tiedoston alkioiden lukumäärä "piilotettua" vain yhteen paikkaan. Tällaisten rakenteiden käyttö on kuitenkin vähän "vaarallista", koska pitää tarkkaan miettiä kelpaako sama homma todellakin sekä lukemiseen että kirjoittamiseen. Usein onkin parempi kirjoittaa ensin erilliset lukemiset ja kirjoittamiset ja tämän jälkeen koota tarvittaessa yhteiset osat samaan aliohjelmaan tai makroon. Tehtäviä: 1) Kirjoita kaikki tiedoston makrot auki niinkuin ne oikeasti tulevat esiintymään (huomaa makrojen sisäkkäisyys). *****************************************************************************/ #include #include #include "savepos.h" #include "mjonot.h" /****************************************************************************/ int WINAPI_LIB ForceInsideScreen(RECT FAR *rc) /* Pakotetaan ikkuna, jonka koordinaatit ovat rc:ssa, näkymään ainakin ** osittain ruudun alueella siten, että siihen ainakin päästään käsiksi, ** eli joko oikea tai vasen yläkulma on ruudussa. Tällöin ikkuna saadaan ** ainakin aktivoiduksi ja tämän jälkeen voidaan painaa ALT-SPACE ja saadaan ** liikutus näppäimistöltä käyntiin. ** Palautetaan montaako koordinaattia jouduttiin muuttamaan. (0= oli sisällä) */ { #define INSIDE 10 /* Paljonko ikkunaa tuodaan sisäänpäin */ RECT sc; int width=rc->right-rc->left, height=rc->bottom-rc->top; int i = 0; GetWindowRect(GetDesktopWindow(),&sc); if ( rc->left >= sc.right ) { i++; rc->left = sc.right-INSIDE; } if ( rc->right <= sc.left ) { i++; rc->left = sc.left-width+INSIDE; } if ( rc->top >= sc.bottom ) { i++; rc->top = sc.bottom-INSIDE; } if ( rc->bottom <= sc.top ) { i++; rc->top = sc.top; } rc->right = rc->left + width; rc->bottom = rc->top + height; return i; } /****************************************************************************/ static int MakeName(HWND hWnd,char *s,int maxs,LPCSTR name, char *ext) /* Jos name != NULL niin s = name, muuten ohjelman nimi -.EXE + ext */ { char *p; if ( name ) { _fstrncpy(s,name,maxs); s[maxs-1]=0; return 0; } GetModuleFileName(GetWindowInstance(hWnd),s,maxs); /* strncpy(s,_argv[0],maxs); s[maxs-1]=0; Ei toimi DLL:ssä! */ p = strstr(s,".exe"); if ( !p ) p = strstr(s,".EXE"); if ( !p ) return 1; strncpy(p,ext,5); p[4]=0; return 0; } /****************************************************************************/ #define RC wndpl.rcNormalPosition /****************************************************************************/ /* Makro INI_WNDPL määrittää tarvittavat muuttujat ja laittaa niiden */ /* arvot oikein nykyisen ikkunan paikan mukaan. Tämän jälkeen joko luetaan */ /* tai kirjoitetaan INI-tiedostoon makrolla INI. INI pitää olla oikein */ /* määritelty ennen tämän makron kutsua. */ #define INI_WNDPL \ int width,height; \ char inifilename[80]; \ WINDOWPLACEMENT wndpl; \ MakeName(hWnd,N_S(inifilename),Inifilename,".INI"); \ \ wndpl.length = sizeof(wndpl); /* Muista! Muuten ei toimi! */ \ GetWindowPlacement(hWnd, &wndpl); \ width = RC.right-RC.left; height = RC.bottom-RC.top; \ \ INI(RC.left,"x"); INI(RC.top,"y"); \ if ( size ) { \ INI(width,"leveys"); INI(height,"korkeus"); \ } \ INI(wndpl.showCmd,"minmax"); \ /****************************************************************************/ /****************************************************************************/ /* Makro INI lukee kokonaisluvun inifilestä kohdasta entry. */ /* Oletuksena käytetään lukua itseään. */ #define INI(i,entry) i=GetPrivateProfileInt(section,entry,i,inifilename); /****************************************************************************/ void WINAPI_LIB RestorePos(HWND hWnd,LPCSTR Inifilename,LPCSTR section, tSaveposSizeEnum size) /* Aliohjelmalla palautetaan ikkuna siihen kokoon, paikkaan ja tilaan, joka ** on talletettu tiedostoon inifilename kohtaan section ** Ongelmia: ** 1. Jos tietoja ei ole tallettu, ei saa muuttaa vastaavaa arvoa! ** Makro HAE hoitaa tämän ** 2. Jos ikkuna on joutunut näytön ulkopuolella, pitäisi se saada näytön ** sisälle. ** Ratkaisu: Selvitetään ensin ikkunan nykyhetken tiedot ja sitten ** käytetään oletusarvoina nykytietoja. Jos jokin niistä puuttuu ** INI-tiedostosta, jää sille nykyinen arvo. ** Jos ei-lapsi-ikkuna meinaa joutua ruudun ulkopuolelle, pakotetaan se ** ruudun sisälle ainakin hieman. */ { INI_WNDPL; RC.right=RC.left+width; RC.bottom=RC.top+height; if ( GetParent(hWnd) == NULL ) ForceInsideScreen(&RC); // wndpl.showCmd=SW_HIDE; SetWindowPlacement(hWnd, &wndpl); } #undef INI /****************************************************************************/ /* Makro K kirjoittaa annetun kokonaisluvun inifileen kohtaan entry */ #define INI(i,entry) {char s[20]; wsprintf(s,"%d",i);\ WritePrivateProfileString(section,entry,s,inifilename);} /****************************************************************************/ void WINAPI_LIB SavePos(HWND hWnd,LPCSTR Inifilename,LPCSTR section, tSaveposSizeEnum size) /* Talletetaan ikkunan paikka INI-tiedostoon. ** Onglmia jos käytetään GetWindowRect-kutsua ** 1. Jos ikkuna on minimoitu tai maksimoitu, pitää se ensin palauttaa ** Normaaliin kokoonsa sen paikan määräämikseksi ja sitten palauttaa ** ikkuna jälleen takaisin. ** 2. Jos ikkuna on lapsi-ikkuna, antaa GetWindowRect sen tiedot ** näytön koordinaateissa ja MoveWindow taas palautusvaiheessa ** tarvitsee tiedot Client-koordinaateissa (suhteessa isäikkunaan) ** 3. Jos ohjelma on loppumassa (viesti WM_DESTROY), on ikkuna piilotettu. ** Jos minimoitu ikkuna palautetaan mittaamista varten normaaliin ** kokoonsa, ja ohjelma on loppumssa, on ikkuna piilotettu. Se pitää ** siis myös jättää piilotetuksi tämän aliohjelman jälkeen tai siitä ** jää näyttöön ikkunan haamu ohjelman loppumisen jälkeen. ** 4. Aliohjelmakutsu pitää voida tehdä myös kesken ohjelman, ilman ** että ohjelmasta ollaan postumassa. ** Ratkaisu: Kaikki ongelmat postuvat kun käytetään kutsua ** GetWindowPlacement */ { INI_WNDPL; } #undef INI