Ncurses: Menüs
<<< ncurses-Startseite | ncurses | << Inhaltsverzeichnis |
< Panels | Formulare > |
Ein ncurses-Menü ist ein Menü in der ursprünglichen Bedeutung, also eine einfache Auswahlbox. Allerdings bietet auch die menu-Bibliothek genügend Möglichkeiten um diese Auswahlboxen sehr schön und sinnvoll auszugestalten.
Ein Menü erzeugen und wieder löschen
[Bearbeiten]Die Erzeugung eines Menüs untergliedert sich in folgende Schritte:
- Speicherplatz für die Items (Menüeinträge) reservieren:
calloc
-Funktion - Items erzeugen:
ITEM *new_item(const char *name, const char *description);
- Menü erzeugen:
MENU *new_menu(ITEM **items);
- Menü "posten" (post = anheften, ankleben -> "PostIt") :
int post_menu(MENU *menu);
Das "Abbauen" eines Menüs geschieht in umgekehrter Reihenfolge:
- Menü "unposten":
int unpost_menu(MENU *menu);
- Menü freigeben:
int free_menu(MENU *menu);
- Items freigeben:
int free_item(ITEM *item);
- Reservierten Items-Speicherplatz freigeben:
free
-Funktion
Der Menü-Treiber
[Bearbeiten]Eingabeereignisse für das Menü können mit der Funktion
int menu_driver(MENU *menu, int c);
abgehandelt werden.
Der Parameter c
bstimmt, welche Menüaktion durchgeführt werden soll:
REQ_LEFT_ITEM | bewegt den Menücursor um einen Eintrag nach links |
REQ_RIGHT_ITEM | bewegt den Menücursor um einen Eintrag nach rechts |
REQ_UP_ITEM | bewegt den Menücursor um einen Eintrag nach oben |
REQ_DOWN_ITEM | bewegt den Menücursor um einen Eintrag nach unten |
REQ_SCR_ULINE | eine Zeile aufwärts scrollen |
REQ_SCR_DLINE | eine Zeile abwärts scrollen |
REQ_SCR_UPAGE | eine Seite aufwärts scrollen |
REQ_SCR_DPAGE | eine Seite abwärts scrollen |
REQ_FIRST_ITEM | bewegt den Menücursor zum ersten Eintrag |
REQ_LAST_ITEM | bewegt den Menücursor zum letzten Eintrag |
REQ_NEXT_ITEM | bewegt den Menücursor zum nächsten Eintrag |
REQ_PREV_ITEM | bewegt den Menücursor zum vorherigen Eintrag |
REQ_TOGGLE_ITEM | An- oder abwählen eines Eintrags |
REQ_CLEAR_PATTERN | Suchmusterpuffer löschen |
REQ_BACK_PATTERN | Das vorherige Zeichen aus dem Suchmusterpuffer löschen |
REQ_NEXT_MATCH | Menücursor zum nächsten Eintrag, der zum Suchmuster passt, bewegen |
REQ_PREV_MATCH | Menücursor zum vorigen Eintrag, der zum Suchmuster passt, bewegen |
Den aktuell angewählten Menüeintrag ermitteln
[Bearbeiten]ITEM *current_item(const MENU *menu); int item_index(const ITEM *item);
Beispiel
[Bearbeiten]#include <menu.h> #include <stdlib.h> ITEM **it; MENU *me; void quit(void) { int i; unpost_menu(me); free_menu(me); for(i=0; i<=4; i++) { free_item(it[i]); } free(it); endwin(); } int main(void) { int ch; initscr(); atexit(quit); clear(); noecho(); curs_set(0); cbreak(); nl(); keypad(stdscr, TRUE); it = (ITEM **)calloc(5, sizeof(ITEM *)); it[0] = new_item("M1", ""); it[1] = new_item("M2", ""); it[2] = new_item("M3", ""); it[3] = new_item("Ende", ""); it[4] = 0; me = new_menu(it); post_menu(me); mvaddstr(7, 3, "Programm mittels Menü oder F1-Funktionstaste beenden"); refresh(); while((ch=getch()) != KEY_F(1)) { switch(ch) { case KEY_DOWN: menu_driver(me, REQ_DOWN_ITEM); break; case KEY_UP: menu_driver(me, REQ_UP_ITEM); break; case 0xA: /* Return- bzw. Enter-Taste -> ASCII-Code */ if(item_index(current_item(me)) == 3) exit(0); } } return (0); }
Compilieren, Linken:
gcc -o bsp bsp.c -lmenu -lncurses
Das Menü formatieren
[Bearbeiten]Ein Menü ist standardmäßig bis zu 16 Zeilen hoch und 1 Spalte breit. Zur Einstellung einer anderen Größe, z.B. zur Generierung eines mehrspaltigen Menüs, existiert die Funktion
int set_menu_format(MENU *menu, int rows, int cols);
Diese Funktion muss im Bedarfsfall aufgerufen werden, bevor das Menu gepostet wird.
Beispiel
[Bearbeiten]#include <menu.h> #include <stdlib.h> ITEM **it; MENU *me; void quit(void) { int i; unpost_menu(me); free_menu(me); for(i=0; i<=4; i++) { free_item(it[i]); } free(it); endwin(); } int main(void) { int ch; initscr(); atexit(quit); clear(); noecho(); curs_set(0); cbreak(); nl(); keypad(stdscr, TRUE); it = (ITEM **)calloc(5, sizeof(ITEM *)); it[0] = new_item("M1", ""); it[1] = new_item("M2", ""); it[2] = new_item("M3", ""); it[3] = new_item("Ende", ""); it[4] = 0; me = new_menu(it); set_menu_format(me, 2, 2); post_menu(me); mvaddstr(7, 3, "Programm mittels Menü oder F1-Funktionstaste beenden"); refresh(); while((ch=getch()) != KEY_F(1)) { switch(ch) { case KEY_DOWN: menu_driver(me, REQ_DOWN_ITEM); break; case KEY_UP: menu_driver(me, REQ_UP_ITEM); break; case KEY_RIGHT: menu_driver(me, REQ_RIGHT_ITEM); break; case KEY_LEFT: menu_driver(me, REQ_LEFT_ITEM); break; case 0xA: /* Return- bzw. Enter-Taste -> ASCII-Code */ if(item_index(current_item(me)) == 3) exit(0); } } return (0); }
Das Markierungssymbol
[Bearbeiten]Zwecks besserer Sichtbarkeit wird vor dem ausgewählten Menüeintrag zusätzlich ein Zeichen oder eine Zeichenkette als Markierungssymbol gesetzt. Normalerweise ist dies ein Bindestrich. Dieses Verhalten kann aber mit der Funktion
int set_menu_mark(MENU *menu, const char *mark);
geändert werden. Diese Funktion wird nur dann das gewünschte Resultat liefern, wenn sie vor dem Posten des Menüs aufgerufen wird.
Beispiel (Programmausschnitt)
[Bearbeiten]// ... set_menu_mark(me, "-->"); post_menu(me); // ...
Beispiel (Programmausschnitt)
[Bearbeiten]// Ausschalten des Markierungssysmbols //... set_menu_mark(me, ""); post_menu(me); // ...
Das Markierungssymbol sollte aber nur dann ausgeschaltet werden, wenn absolut sichergestellt ist, dass das Programm nur auf Terminals mit "Highlighting"- oder Farbunterstützung eingesetzt wird. Das "Erraten" der jeweiligen Menücursorposition kann sich sonst sehr nervenaufreibend gestalten.
Menüfenster
[Bearbeiten]Jedes Menü kann mit einem Haupt- und Unterfenster verknüpft werden. Das Hauptfenster kann z.B. zur Aufnahme eines Menütitels und zur Umrahmung des Unterfensters dienen. Im Unterfenster werden die Menüeinträge dargestellt.
int set_menu_win(MENU *menu, WINDOW *win); int set_menu_sub(MENU *menu, WINDOW *sub);
Werden diese Funktionen in einem Programm nicht eingesetzt, so sind die Menüs mit dem Standardscreen verbunden.
Beispiel
[Bearbeiten]#include <menu.h> #include <stdlib.h> ITEM **it; MENU *me; WINDOW *win; void quit(void) { int i; unpost_menu(me); free_menu(me); for(i=0; i<=4; i++) { free_item(it[i]); } free(it); delwin(win); endwin(); } int main(void) { int ch; initscr(); atexit(quit); clear(); noecho(); curs_set(0); cbreak(); nl(); keypad(stdscr, TRUE); it = (ITEM **)calloc(5, sizeof(ITEM *)); it[0] = new_item("M1", "Menueeintrag 1"); it[1] = new_item("M2", "Menueeintrag 2"); it[2] = new_item("M3", "Menueeintrag 3"); it[3] = new_item("Ende", "Programm beenden"); it[4] = 0; me = new_menu(it); win = newwin(8, 30, 5, 5); set_menu_win (me, win); set_menu_sub (me, derwin(win, 4, 28, 3, 2)); box(win, 0, 0); mvwaddstr(win, 1, 2, "***** Testmenü *****"); post_menu(me); mvaddstr(14, 3, "Programm mittels Menü oder F1-Funktionstaste beenden"); refresh(); wrefresh(win); while((ch=getch()) != KEY_F(1)) { switch(ch) { case KEY_DOWN: menu_driver(me, REQ_DOWN_ITEM); break; case KEY_UP: menu_driver(me, REQ_UP_ITEM); break; case 0xA: /* Return- bzw. Enter-Taste -> ASCII-Code */ if(item_index(current_item(me)) == 3) exit(0); } wrefresh(win); } return (0); }
Die minimal notwendige Größe eines Unterfensters kann über die Funktion
int scale_menu(const MENU *menu, int *rows, int *columns);
ermittelt werden.
Menüs bunt gestalten
[Bearbeiten]Die Farbgebung und Darstellungsattribute des selektierten Items festlegen:
int set_menu_fore(MENU *menu, chtype attr);
Die Farbgebung und Darstellungsattribute der unselektierten Items festlegen:
int set_menu_back(MENU *menu, chtype attr);
Das Erscheinungsbild des Menühauptfensters kann natürlich über die konventionellen ncurses-Befehle (wbkgd, wattrset
, etc.) gestaltet werden.
Beispiel
[Bearbeiten]#include <menu.h> #include <stdlib.h> ITEM **it; MENU *me; WINDOW *win; void quit(void) { int i; unpost_menu(me); free_menu(me); for(i=0; i<=4; i++) { free_item(it[i]); } free(it); delwin(win); endwin(); } int main(void) { int ch; initscr(); atexit(quit); clear(); noecho(); curs_set(0); cbreak(); nl(); keypad(stdscr, TRUE); start_color(); init_pair(1, COLOR_WHITE, COLOR_BLUE); init_pair(2, COLOR_BLUE, COLOR_YELLOW); bkgd(COLOR_PAIR(1)); it = (ITEM **)calloc(5, sizeof(ITEM *)); it[0] = new_item("M1", "Menueeintrag 1"); it[1] = new_item("M2", "Menueeintrag 2"); it[2] = new_item("M3", "Menueeintrag 3"); it[3] = new_item("Ende", "Programm beenden"); it[4] = 0; me = new_menu(it); win = newwin(8, 30, 5, 5); set_menu_win (me, win); set_menu_sub (me, derwin(win, 4, 28, 3, 2)); box(win, 0, 0); mvwaddstr(win, 1, 2, "***** Testmenü *****"); set_menu_fore(me, COLOR_PAIR(1)|A_REVERSE); set_menu_back(me, COLOR_PAIR(1)); wbkgd(win, COLOR_PAIR(2)); post_menu(me); mvaddstr(14, 3, "Programm mittels Menü oder F1-Funktionstaste beenden"); refresh(); wrefresh(win); while((ch=getch()) != KEY_F(1)) { switch(ch) { case KEY_DOWN: menu_driver(me, REQ_DOWN_ITEM); break; case KEY_UP: menu_driver(me, REQ_UP_ITEM); break; case 0xA: /* Return- bzw. Enter-Taste -> ASCII-Code */ if(item_index(current_item(me)) == 3) exit(0); } wrefresh(win); } return (0); }
Optionen für Menüeinträge
[Bearbeiten]int set_item_opts(ITEM *item, OPTIONS opts); int item_opts_on(ITEM *item, OPTIONS opts); int item_opts_off(ITEM *item, OPTIONS opts);
Die Funktionsbezeichnungen sind selbsterklärend. Es gibt hier nur eine Option, nämlich
O_SELECTABLE
Wird die Selektierbarkeit für einen Menüeintrag hiermit ausgeschaltet, so ist dieser Menüeintrag als nicht selektierbar dargestellt. Dies bezieht sich aber nur auf die Menüdarstellung, das Item ist trotzdem immer noch auswählbar. Die "Nichtselektierbarkeit" eines Items muss vom Programmierer im weiteren Code berücksichtigt werden. Möglich ist dies durch die Abfrage der Itemoptionen
OPTIONS item_opts(const ITEM *item);
Die Farbgebung für derartige nicht selektierbare Items kann über die Funktion
int set_menu_grey(MENU *menu, chtype attr);
gesteuert werden.
Beispiel
[Bearbeiten]#include <menu.h> #include <stdlib.h> ITEM **it; MENU *me; WINDOW *win; void quit(void) { int i; unpost_menu(me); free_menu(me); for(i=0; i<=4; i++) { free_item(it[i]); } free(it); delwin(win); endwin(); } int main(void) { int ch; initscr(); atexit(quit); clear(); noecho(); curs_set(0); cbreak(); nl(); keypad(stdscr, TRUE); start_color(); init_pair(1, COLOR_WHITE, COLOR_BLUE); init_pair(2, COLOR_BLUE, COLOR_YELLOW); init_pair(3, COLOR_BLACK, COLOR_BLUE); bkgd(COLOR_PAIR(1)); it = (ITEM **)calloc(5, sizeof(ITEM *)); it[0] = new_item("M1", "Menueeintrag 1"); it[1] = new_item("M2", "Menueeintrag 2"); it[2] = new_item("M3", "Menueeintrag 3"); it[3] = new_item("Ende", "Programm beenden"); it[4] = 0; item_opts_off(it[3], O_SELECTABLE); me = new_menu(it); win = newwin(8, 30, 5, 5); set_menu_win (me, win); set_menu_sub (me, derwin(win, 4, 28, 3, 2)); box(win, 0, 0); mvwaddstr(win, 1, 2, "***** Testmenü *****"); set_menu_fore(me, COLOR_PAIR(1)|A_REVERSE); set_menu_back(me, COLOR_PAIR(1)); set_menu_grey(me, COLOR_PAIR(3)); wbkgd(win, COLOR_PAIR(2)); post_menu(me); mvaddstr(14, 3, "Programm mittels F1-Funktionstaste beenden"); refresh(); wrefresh(win); while((ch=getch()) != KEY_F(1)) { switch(ch) { case KEY_DOWN: menu_driver(me, REQ_DOWN_ITEM); break; case KEY_UP: menu_driver(me, REQ_UP_ITEM); break; case 0xA: /* Return- bzw. Enter-Taste -> ASCII-Code */ if(item_index(current_item(me))==3 && item_opts(current_item(me))==O_SELECTABLE) exit(0); } wrefresh(win); } return (0); }
Menüoptionen
[Bearbeiten]Zum Setzen von Menüoptionen werden diese Funktionen verwendet:
int set_menu_opts(MENU *menu, OPTIONS opts); int menu_opts_on(MENU *menu, OPTIONS opts); int menu_opts_off(MENU *menu, OPTIONS opts);
Einige der verfügbaren Optionswerte sind
O_SHOWDESC |
Beschreibungen zu den Einträgen anzeigen |
O_NONCYCLIC |
Nicht-zyklisch, am Menüende nicht automatisch zum Menübeginn springen und umgekehrt |
O_ONEVALUE |
Nur ein Menüeintrag ist auswählbar |
Alle Optionen sind standardmäßig eingeschaltet.
Beispiel: O_SHOWDESC
[Bearbeiten]// ... menu_opts_off(me, O_SHOWDESC); // ...
Beispiel: Mehrfachauswahl
[Bearbeiten]Bei einer möglichen Mehrfachauswahl kann nicht mehr einfach mittels current_item
der ausgewählte Menüeintrag bestimmt werden. Stattdessen kann die Funktion
bool item_value(const ITEM *item);
Verwendung finden. Diese Funktion liefert für selektierte Menüeinträge TRUE
, für unselektierte Menüeinträge FALSE
.
// ... menu_opts_off(me, O_ONEVALUE); // ... while((ch=getch()) != KEY_F(1)) { switch(ch) { // ... case 0x20: /* Leertaste */ menu_driver(me, REQ_TOGGLE_ITEM); int i; for(i=0; i<4; i++) { mvprintw(16+i, 3, "Item %i: %i", i+1, item_value(it[i])); } break; // ... } } //...
Damit sind die ncurses-Menüs zwar bei weitem noch nicht umfassend abgehandelt. Jedoch wären weitere Themen teilweise schon sehr komplex und nur für spezielle Einsatzfälle wirklich interessant. Mit dieser Bemerkung soll dieses Kapitel hier vorerst abgeschlossen werden.
<<< ncurses-Startseite | ncurses | << Inhaltsverzeichnis |
< Panels | Formulare > |