/****************************************************************************/ /* ** M E N U T . C ** ** Aliohjelmia menutjen tekemiseen. ** ** Aliohjelmat: ** InitMenuSystem - alustaa menusysteemin ** GetMessage - k„ytt„„ menusysteemi„ ** DispatchMessage - toteuttaa aliohjelman ** PostQuitMessage - poistaa menuille varatut tilat ** ** Tekij„: Vesa Lappalainen 25.1.1994 ** Muutettu: ** Mit„ muutettu: ** ** Mit„ vikaa tai mit„ tekem„tt„: ** - Menujen poisto homman loputtua ** - Kello, pvm yms. toimimaan, nyt kello antaa aina 23.20 ** - gotojen siivoaminen (silloin kun se todella siivoaa) ** - aliohjelmien kommentointi ja parametrien dokumentointi ** - k„ytet„„n viel„ joissakin paikoissa GMenu->MenuS, jolloin ** tietoja EI voi v„litt„„ msg-parametrin avulla kuten oli ** alunperin ajatus ** - mahdollisuus k„ytt„„ my”s ilman koko msg-parametria (eli jos ** se on NULL ** - numeropohjaiset itemit toteuttamatta (tarvitaan jos ** p„„ohjelma halutaan switch-toimivaksi) ** - automaattinen lopetus? ** - p„„sy suoraan menun lehdest„ p„„menuun tai ehk„ jopa ** menusysteemiin merkint„ "CALL", "GOTO" ** - menun k„ytt” ohjelmasta k„sin alkaen jostakin toisesta ** kohdasta (rinnakkainen k„sittely) ** - menut.h ** - @-komentojen dokumentointi ja niiden lis„„misen dokumentointi ** - muita @-komentoja, kuten esim. v„ri ** - mit„ tapahtuu valinnan j„lkeen? Menu-kohtaisesti vai ** item-kohtaisesti? ELi mahdollisuus esim. ruudun tyhjennykseen ** valinnan j„lkeen, tietyn tekstin tulostamiseen yms? ** Vaikuttaa tietenkin vain lehtisolmuihin, kun muissa tulostus ** voidaan hoitaa seuraavan menun alussa. ** - tarvitaanko (lehti)aliohjelmiin joitakin parametreja ja jos ** tarvitan niin mitk„ olisivat hyv„t? ** - komento raamien piirt„miseksi ** - GotoXY toimimaan STD:n mukaisesti ** ** ** K„ytt”: ** ** Projektiin mukaan: ** omat ohjelmat ** mjonot.c ** menut.c ** arvonta.c ** ** Kirjoitetaan seuraavanlainen tiedosto (MENUT.MNU): ** **[] # t„m„ on p„„menu ** Valitse seuraavista: ** 0 = Lopetus |0L|Lopetus ** 1 = Tiedostot |1T|[Tiedostot] ** 2 = Korjailu |2K|[Korjailu] ** 3 = Ikkunat |3I|[Ikkunat] ** ? = Avustus |?A|[Avustus] ** : **[Tiedostot] ** ** Kello on @Kello@ Suomen aikaa. ** ** 0 = P„„menu |0P| ** 1 = Talletus |1T|Talletus ** 2 = Uusi |2U|Uusi ** 3 = Sulje |3S|Sulje ** ? = Avustus |?A|ApuaT **[Korjailu] **... **[Ikkunat] **... **[Avustus] ** 0 = P„„menu |0P| ** 1 = Yleist„ |1Y|ApuaY ** 2 = Sis„llys |2S|ApuaS ** **[Message0] @Goto 5,21@ # Kaikkien viestien 1. riville t„m„ **[Message1] @Goto 5,-1@ # Viestien seuraaville riveille t„m„ **[Virhe1] Levyvirhe @%0@ Jatketaanko |KE| **[Virhe2] Muisti loppui, paina jotakin |*| **[Virhe*] Tuntematon virhe! Paina jotakin |*| **[Odota] Paina jotakin! |*| **[Ilmoitus] T„m„ ei odota mit„„n! ** ** Ohjelmaan kirjoitetaan: **------------------------- ** #include "menut.h" ** ** tMenuTable menu_table[] = { ** {"Talletus",0,talletus}, ** {"Uusi" ,0,uusi }, ** {"Sulje" ,0,sulje }, ** {"ApuaT" ,0,ApuaT }, ** ... ** {"ApuaS" ,0,ApuaS }, ** {"Lopetus" ,0,Lopetus }, ** {NULL ,0,NULL } ** } ** ** void Lopetus(void) ** { ** PostQuitMessage(0); ** } ** ** int main(...) ** { ** MSG msg; ** if ( InitMenuSystem("menut.mnu",&msg,menu_table) ) return 1; ** while ( GetMessage(&msg) ) { ** DispatchMessage(&msg); ** } ** return msg.wParam; ** } */ #include #include #include #include "mjonot.h" #include "arvonta.h" #ifdef __TURBOC__ #define GETCH #define CLRSCR #include #endif /****************************************************************************/ char lue_merkki(void) /* N„pp„imist”lt„ luettu merkki. */ /* Funktiolla luetaan n„pp„imist”lt„ yksi merkki. Mik„li ohjelmassa ** ei ole m„„ritelty vakiota GETCH, vaaditaan RET-n„pp„imen painallus. ** Globaalit: GETCH - m„„ritys ** Sy”tt”: N„pp„imist” ----------------------------------------------------------------------------*/ { /* Seuraava toimii mm. Turbo C:ss„: */ #ifdef GETCH return getch(); #else /* Seuraava on standardin mukainen: */ char s[50]; fgets(s,50,stdin); return s[0]; #endif } /****************************************************************************/ #define SCRX 5 #define SCRY 21 typedef struct { int x; int y; char scr[25][80]; } tScreen; static tScreen Screen; /****************************************************************************/ char *PyyhiRuutu(char *s) { if ( s ) s++; #ifdef CLRSCR clrscr(); /* EPŽS */ #else { int i; for (i=0; i<25; i++) printf("\n"); } #endif memset(Screen.scr,' ',80*25); Screen.x = 0; Screen.y = 0; return NULL; } /****************************************************************************/ int printscr(const char *st) { char s[300],*sp=s, *p; int j,l; kopioi_jono(N_S(s),st); while (1) { p = palanen(sp,"\n",&j); l = strlen(p); if ( Screen.x+l >= 80 ) l = 80 - Screen.x; if ( l < 0 ) l = 0; p[l] = 0; memcpy(&Screen.scr[Screen.y][Screen.x],p,l); gotoxy(Screen.x+1,Screen.y+1); printf(p); Screen.x += l; if ( j <= 0 ) return; Screen.x = 0; Screen.y++; if ( Screen.y > 24 ) Screen.y = 24; printf("\n"); sp = NULL; }; } /****************************************************************************/ int ScrMove(int x,int y) { gotoxy(x,y); Screen.x = x; Screen.y = y; return 0; } /****************************************************************************/ typedef void (*pMenuFunc)(void); typedef struct { char *sitem; int item; pMenuFunc func; } tMenuTable; typedef struct { char *ItemText; char *Choose; char *sitem; int item; } tMenuItem; #define MAX_ITEMS 25 #define MAX_MENUS 200 #define MAX_MENUSTACK 10 typedef struct { char *MenuName; int nItems; /* Valintojen lkm */ int rows; /* Rivien lkm */ int message; /* Onko viesti vai menu */ tMenuItem Items[MAX_ITEMS]; } tMenu; typedef struct { tMenu **menu; tMenu *CurrentMenu; int nmenu; tMenuTable *MenuTable; int sTop; tMenu *Stack[MAX_MENUSTACK]; tMenu *Main; } tMenuSystem; typedef struct { char *sitem; int item; int wParam; tMenuSystem *MenuSystem; } MSG; typedef struct { int QuitMsg; int QuitMenu; tMenuSystem MenuSystem; } tMenuStatic; /****************************************************************************/ static tMenuStatic GMenu = {-1,0}; static char EmptySt[1] = ""; #define MAX_PARAMS 10 static char Params[MAX_PARAMS][50]; /****************************************************************************/ void SetMessageParam(int n,const char *s) { if ( s == NULL ) return; if ( n < 0 || MAX_PARAMS <= n ) return; kopioi_jono(N_S(Params[n]),s); } /****************************************************************************/ const char *GetMessageParam(int n) { if ( n < 0 || MAX_PARAMS <= n ) return EmptySt; return Params[n]; } /****************************************************************************/ const char *Kello(char *s) { if ( s ) s++; return "23:20"; } /****************************************************************************/ const char *Parameter(char *s) { return GetMessageParam(atoi(s+1)); } /****************************************************************************/ const char *DoGoto(char *s) { int x=0,y=-1; sscanf(s,"Goto %d,%d",&x,&y); if ( x < 0 ) x = Screen.x; if ( y < 0 ) y = Screen.y; ScrMove(x,y); return EmptySt; } const char *TulostaTeksti(char *s); void PrintMenu(tMenu *menu); typedef const char * (*pSpecFunc)(char *); typedef struct { char *command; pSpecFunc func; } tSpecialTable; tSpecialTable SpecialTable[] = { {"Clear" ,PyyhiRuutu }, {"[*]" ,TulostaTeksti }, {"Kello" ,Kello }, {"%*" ,Parameter }, {"Goto *" ,DoGoto }, {NULL ,NULL } }; /****************************************************************************/ int MenuPrintf(char *s) /* Tulostetaan merkkijono siten, ett„ suoritetaan siin„ mahdollisesti ** olevat @-komennot. Saattaa johtaa rekursioon! ** Palutetaan 0 jos rivi ei p„„ty @ ja 1 jos p„„ttyy */ { char *pa,*pl=NULL; const char *ps; int i; do { if ( ( pa = strchr(s,'@') ) == NULL ) { printscr(s); return ( pl && s[0]==0 ); } *pa = 0; pa++; printscr(s); if ( ( pl = strchr(pa,'@') ) != NULL ) { *pl = 0; pl++; } for (i=0; SpecialTable[i].command; i++) if ( wildmat(pa,SpecialTable[i].command) == 0 ) { ps = SpecialTable[i].func(pa); if ( ps && ps[0] ) printscr(ps); break; } *(pa-1) = '@'; if ( pl ) *(pl-1) = '@'; s = pl; } while ( s != NULL ); return 0; } /****************************************************************************/ void PrintMenu(tMenu *menu) { int i,m; char *p; for (i=0; i < menu->rows; i++) { if ( menu->message ) { /* Viesteille suoritetaan [Message?] rivi */ char s[30]; int j; for (j=i; j>=0; j--) { /* Etsit„„n suurin [Message?] */ sprintf(s,"[Message%d]",j); if ( TulostaTeksti(s) ) break; } } p = menu->Items[i].ItemText; m = MenuPrintf(p); if ( m == 0 && i < menu->rows-1 ) printscr("\n"); } } /****************************************************************************/ tMenu *FindMenu(tMenu **menus,const char *s) { int i; if ( s == NULL ) return NULL; for (i=0; menus[i]; i++) { if ( strcmp(menus[i]->MenuName,s) == 0 ) return menus[i]; } return NULL; } /****************************************************************************/ const char *TulostaTeksti(char *s) { tMenu *menu = FindMenu(GMenu.MenuSystem.menu,s); if ( !menu ) return NULL; PrintMenu(menu); return EmptySt; } /****************************************************************************/ void PushMenu(tMenuSystem *MenuS, tMenu *menu) { if ( MenuS->sTop >= MAX_MENUSTACK ) return; MenuS->Stack[MenuS->sTop++] = menu; } /****************************************************************************/ tMenu *PopMenu(tMenuSystem *MenuS) { if ( MenuS->sTop <= 0 ) return NULL; return MenuS->Stack[--MenuS->sTop]; } /****************************************************************************/ int PostQuitMessage(int n) { GMenu.QuitMsg = n; GMenu.QuitMenu = 1; return 0; } /****************************************************************************/ int InitMenuSystem(char *name,MSG *msg,tMenuTable *MenuTable) { #define NEXT(s) ( p = poista_lopputyhjat(palanen(s,"|",&j)),\ p[0] == 0 ? EmptySt : tee_jono(p) ) tMenu *menu; FILE *f; char s[300]; char *p,c; int j; int items; int ret = 2; tMenuSystem *MenuS = &(GMenu.MenuSystem); f = fopen(name,"rt"); if ( f == NULL ) return 1; MenuS->nmenu = 0; MenuS->MenuTable = MenuTable; MenuS->CurrentMenu = NULL; MenuS->menu = calloc(sizeof(tMenu *),MAX_MENUS); if ( ! MenuS->menu ) goto closeMenuFile; ret = 0; while ( 1 ) { if ( f_lue_jono(f,N_S(s)) < 0 ) break; newMenu: if ( MenuS->nmenu > 0 ) { /* Poistetaan turha koko edellisest„ menusta */ menu = MenuS->menu[MenuS->nmenu-1]; MenuS->menu[MenuS->nmenu-1] = realloc(menu,sizeof(tMenu) - (MAX_ITEMS-menu->rows)*sizeof(tMenuItem)); } if ( MenuS->nmenu >= MAX_MENUS ) break; if ( s[0] != '[' ) continue; p = palanen(s,"#",&j); menu = calloc(sizeof(tMenu),1); if ( menu == NULL ) break; MenuS->menu[MenuS->nmenu++] = menu; p = strchr(s,']'); if ( p != NULL ) { c = *(++p); *p = 0; } menu->MenuName = tee_jono(poista_lopputyhjat(s)); items = 0; menu->nItems = 0; menu->rows = 0; menu->message= 0; if ( p ) { /* Onko samalla rivill„ muuta => viestimuoto! */ *p = c; poista_lopputyhjat(p); if ( p[0] ) { p++; /* Alkuv„li”y”nti pois */ memmove(s,p,strlen(p)+1); if ( strstr(menu->MenuName,"[Message") == NULL ) menu->message=1; goto itemText; } } while ( 1 ) { if ( f_lue_jono(f,N_S(s)) < 0 ) goto closeMenuFile; itemText: if ( s[0] == '#' ) continue; if ( s[0] == '[' ) goto newMenu; if ( items >= MAX_ITEMS ) continue; p = palanen(s,"#",&j); menu->Items[items].ItemText = NEXT(s); menu->Items[items].Choose = NEXT(NULL); menu->Items[items].sitem = NEXT(NULL); if ( menu->Items[items].Choose != EmptySt ) menu->nItems++; items++; menu->rows = items; } } closeMenuFile: msg->MenuSystem = MenuS; fclose(f); return ret; } /****************************************************************************/ int ClearMenus(MSG *msg) { /* T„ss„ t„ytyisi vapauttaa kaikki tietorakenteet!!!! */ msg->MenuSystem = NULL; return 0; } /****************************************************************************/ int WaitMenuKey(tMenu *menu,char *ex,int *j) { char c; int i; while ( 1 ) { c = isoksi(lue_merkki()); for (i=0; irows; i++ ) if ( ( *j = paikka(menu->Items[i].Choose,c) ) >= 0 ) return i; if ( ( *j = paikka(ex,c) ) >= 0 ) return -1; for (i=0; irows; i++ ) if ( ( *j = paikka(menu->Items[i].Choose,'*') ) >= 0 ) return i; } } /****************************************************************************/ int GetMessage(MSG *msg) { tMenuSystem *MenuS = msg->MenuSystem; tMenu *menu = MenuS->CurrentMenu; tMenu *submenu; int i,j; if ( GMenu.QuitMenu != 0 ) return ClearMenus(msg); while (1) { if ( menu == NULL ) menu = FindMenu(MenuS->menu,"[]"); if ( menu == NULL ) return 0; if ( menu->nItems == 0 ) return 0; PrintMenu(menu); i = WaitMenuKey(menu,"",&j); msg->sitem = menu->Items[i].sitem; if ( msg->sitem == EmptySt ) { menu = PopMenu(MenuS); continue; } if ( msg->sitem[0] != '[' ) break; submenu = FindMenu(MenuS->menu,msg->sitem); if ( submenu == NULL ) continue; if ( submenu->nItems == 0 ) continue; PushMenu(MenuS,menu); menu = submenu; } msg->wParam = 0; MenuS->CurrentMenu = menu; return 1; } /****************************************************************************/ char *make_name(char *s,int s_max,char *topic,char *ex) { kopioi_jono(s,s_max,"["); liita_jono(s,s_max,topic); liita_jono(s,s_max,ex); liita_jono(s,s_max,"]"); return s; } /****************************************************************************/ int ScrMessage(char *topic,int nr) /* ** Funktiolla n„ytet„„n viesti n„ytt””n. ** Paramterit @%n@ korvautuvat parametreill„ ** Viesti on ollut muotoa: ** [DOS1] Dos virhe @%0@. Jatketaanko (K/e) |Ke| ** ** Funktio palauttaa: ** -1 jos ei l”ydy viesti„ [topic+nr] eik„ [topic*] ** 0 jos painetaan 1. valittu kirjain tai return ** 1 jos painetaan 2. kirjain tai space ** 2 jos painetaan 3. kirjain tai ESC ** >2 jos painetaan muita valittavissa olevia kirjaimia */ { char s[80],sn[15]; int i; tMenu *message; sn[0]=0; if ( nr >= 0 ) sprintf(sn,"%d",nr); message = FindMenu(GMenu.MenuSystem.menu,make_name(N_S(s),topic,sn)); if ( !message ) message = FindMenu(GMenu.MenuSystem.menu,make_name(N_S(s),topic,"*")); if ( !message ) return -1; PrintMenu(message); if ( message->nItems == 0 ) return 0; WaitMenuKey(message,"\r \x1b",&i); return i; } /****************************************************************************/ int ScrMessage4(char *topic,int nr,char *p1,char *p2,char *p3,char *p4) { SetMessageParam(0,p1); SetMessageParam(1,p2); SetMessageParam(2,p3); SetMessageParam(3,p4); return ScrMessage(topic,nr); } /****************************************************************************/ int ScrMessage1(char *topic,int nr,char *p1) { return ScrMessage4(topic,nr,p1,NULL,NULL,NULL); } /****************************************************************************/ int ScrMessage2(char *topic,int nr,char *p1,char *p2) { return ScrMessage4(topic,nr,p1,p2,NULL,NULL); } /****************************************************************************/ int ScrMessage3(char *topic,int nr,char *p1,char *p2,char *p3) { return ScrMessage4(topic,nr,p1,p2,p3,NULL); } /****************************************************************************/ int DispatchMessage(MSG *msg) { int i; tMenuSystem *MenuS = msg->MenuSystem; for (i=0;MenuS->MenuTable[i].sitem;i++) { if ( strcmp(MenuS->MenuTable[i].sitem, msg->sitem) == 0 ) MenuS->MenuTable[i].func(); } return 0; } #if 1 /****************************************************************************/ /* Testiosuus */ /****************************************************************************/ //#include "menut.h" void talletus(void) { ScrMessage1("Dos",1,"Talletus"); } void Apua(void) { ScrMessage2("Dos",2,"Apua","Saako sit„?"); } void uusi(void) { SetMessageParam(0,"Uusi"); ScrMessage("Dos",1); } void EiTunne(void) { ScrMessage("Dos",175); } void sulje(void) { ScrMessage3("Dos",3,"Suljetaanko","tiedosto","KOE.DAT"); } void Ilmoitus(void) { ScrMessage("Ilmoitus",1); } void Lopetus(void) { PostQuitMessage(0); } int viesti(char *s) { int i; int c; i = 1; while (1) { c = ScrMessage(s,i); if ( c == 2 ) break; if ( i > 100 ) break; i++; } return c; } void kaikki(void) { viesti("Dos"); viesti("Diskett"); viesti("Bios"); viesti("DiskT"); viesti("Modify"); viesti("Collect"); viesti("File"); viesti("Diskett"); viesti("Disk"); } void kysy(void) { char s[50]; PyyhiRuutu(""); printf("Anna hakusana>"); lue_jono(N_S(s)); while ( viesti(s) != 2 ); } tMenuTable menu_table[] = { {"H_error",0,h_error}, {"Init" ,0,init}, {"Re_amsg" ,0,re_amsg}, {"Setup" ,0,setup}, {"Cre_par",0,cre_par}, /* {"Lst_par",0,lst_par}, */ /* {"Telete",0,telete}, */ {"Ret",0,ret}, {"Veden",0,veden}, {"Cpy",0,cpy}, {"Tir",0,tir}, {"Modify",0,modify}, {"Dsk_rdy" ,0,dsk_rdy}, {"Dsk_free",0,dsk_free}, {"Chk_prn",0,chk_prn}, {"Collect",0,collect}, {"Kysy",0,kysy}, {"Kaikki",0,kaikki}, {"Lopetus" ,0,Lopetus }, {NULL ,0,NULL } }; int main(void) { MSG msg; if ( InitMenuSystem("menut.mnu",&msg,menu_table) ) return 1; while ( GetMessage(&msg) ) { DispatchMessage(&msg); } return msg.wParam; } #endif