NCURSES PROGRAMMING HOWTO

de Pradeep Padala

V.1.3.2 (11/02/2001).


Acest document se intenţioneazǎ a fi un ghid complet pentru programarea cu ncurses şi librǎriile aferente. Vom începe cu un exemplu simplu ca "Hello World"şi vom continua cu lucruri mai complexe. Pornim de la premiza cǎ utilizatorul nu a mai lucrat cu ncurses. Ultima versiune a acestui document se poate gǎsi la  sit-ul web. Trimiteţi comentarii la aceastǎ adresǎ adresǎ


Cuprins

Introducere

În zilele vechi a terminalelor cu caractere, terminalele erau departe de calculatoare şi erau conectate la acestea prin cabluri seriale. Terminalele puteau fi setate prin trimiterea unor şiruri de biţi între ele. Toate capabilităţile (cum ar fi să mutăm cursorul de la o poziţie la alta, ştergerea unei părţi din ecran, scrollarea ecranului, schimbarea modurilor, schimbarea setărilor, culori, luminozitate, clipire, linie de subliniere, video invers etc.) a terminalelor puteau fi accesate prin aceste şirur de biţi care de obicei sunt numiţi  secvenţe escape pentru că încep cu un character escape(0x1B). Chiar astăzi, cu emularea potrivită, putem trimite secvenţe la emulator şi putem avea acelaş effect asupra ferestrei terminal.

Să presupunem că vreţi să afişaţi o linie colorată. Încercaţi să folosiţi consola dumneavoastră.

echo "^[[0;31;40mIn Color"

Primul character este un caracter escape, care arată ca două caractere: ^ şi [. Pentru a putea tipări aceasta trebuie să apăsaţi CTRL+V şi apoi tasta ESC. Toate celelalte sunt caractere normale printabile. Veti putea vedea pe ecran şirul de caractere "In Color" colorat cu roşu. Rămâne aşa şî pentru a reveni la modul normal tipăriţi.

echo "^[[0;37;40m"

Acum, ce înseamnă aceste caractere magice? Greu de înţeles? Pot fi diferite pentru terminale diferite. Aşa încât creatorii Unixului au invetat o tehnică numită termcap. Este un fişier care listează toate capabilităţile unui terminal particular, precum şi secvenţele escape care ne trebuie să obţinem un anumit efect. În ultimii ani, acesta a fost înlocuit cu terminfo. Fără să pătrundem prea mult în detalii, conceptual este să consultăm baza de date terminfo şi să obţinem caracterele de control necesare să fie trimise terminalului sau emulatorului de terminal.

Ce este NCURSES?

Ai putea să te întrebi, care este importanţa acestor vorbării tehnice. În scenariul percendent, fiecare aplicaţie ar trebui să interogheze baza de date terminfo şi să facă lucrurile necesare (trimiterea de caratere de control etc.). Curând a devenit dificil să ne descurcăm în această complexitae şi aşa a apărut 'CURSES'. Curses este o abreviere a "cursor optimization". Librăria Curses formează un înveliş peste lucrul cu codurile de terminal pure, şi asigură un API (Application Programming Interface) foarte flexibil şi eficient. Furnizează funcţiile pentru a muta cursorul, a crea ferestre, produce culori, a se juca cu mouse-ul etc. Programele de aplicaţii nu trebuie nu trebuie să aibă în grijă capabilităţile terminalului.

Aşa ce este NCURSES? NCURSES este o clonă a originalului System V Release 4.0 (SVr4) curses. Este o librărie liber distribuibilă, compatibilă sută la sută cu versiunile mai vechi de curses. Pe scurt, este o librărie de funcţii care administrează afişarea unei aplicaţii pe terminale pazate pe caractere. În restul documentului termenii de curses şi ncurses sunt folosiţi cu acelaşi înţeles.

Pachetul ncurses a fost creat iniţial de Pavel Curtis. Admnistratorul acestui pachet a fost iniţial Zeyd Ben-Halim <zmbenhal@netcom.com>. Eric S. Raymond <esr@snark.thyrsus.com> a scris multe funcţii din versiune de după 1.8.1. Jürgen Pfeifer a scris tot codul despre meniuri şi formulare şi legătura cu Ada95 . Lucrul actual este realizat de Thomas Dickey şi Jürgen Pfeifer. Florian La Roche serveşte ca administrator pentru Fundaţia Free Software, care deţine copyright-ul pentru ncurses. Contactaţi  dezvoltatorii actuali la bug-ncurses@gnu.org.

Ce putem face cu NCURSES

Ncurses nu numai crează un înveliş peste capabilităţile terminalului, dar de asemeni oferă un framework robust pentru a creea interfeţe atrăgătoare (User Interface)s în mod text. Oferă funcţii pentru a creea ferestre etc. Librăriile adiacente panel, menu şi form oferă o extensie la librăria curses. Aceste librării vin de obicei cu curses. Puteţi crea aplicaţii ce conţin ferestre multiple, meniuri, panels şi formulare. Ferestrele pot fi manipulate independent, pot fi 'scrollabile' şi chair şi ascunse.

Meniurile oferă utilizatorului o metodă uşoară de selecţie a comenzilor. Formularele oferă crearea unor ferestre de intrare şi afişare a datelor foarte uşor de folosit. Panel-urile exting capabilităţile ncurses pentru ferestre suprapuse.

Acestea sunt doar câteva simple lucruri pe care puteţi să le realizaţi cu ncurses. Pe parcursul acestui document vom vedea toate capabilităţile acestor librării.

De unde să-l luăm

Bine, acum că ştiţi ce puteţi face cu ncurses, trebuie să fiţi nerăbdători să începeţi. Ncurses vine de obicei inclus în Sistemul de operare. În caz că nu aveţi librăria sau vreţi să o compilaţi chiar voi citiţi mai departe.

Compilaţi pachetul

Ncurses poate fi obţinut de la ftp://ftp.gnu.org/pub/gnu/ncurses/ncurses.tar.gz sau unul din site-urile ftp menţionate în http://www.gnu.org/order/ftp.html. Ultima versiune stabilă este 5.2 20001021.

Citiţi fişierele README şi INSTALL pentru detalii cum să-l instalaţi. De obicei trebuie să urmaţi următorii paşi.

        gunzip ncurses.tar.gz          # unzip the file
        tar -xvf ncurses.tar           # untar the archive
        ./configure            # configure the build according to your 
                               # environment
        make                   # make it
        su -root               # become root
        make install           # install it

Folosiţi RPM

RPM Ncurses poate fi găsit şi downloadat de la http://rpmfind.net/. RPM-ul poate fi instalat cu comanda următoare după ce sunteţi root.

        rpm -i <downloaded rpm>

Scpul acestui document

Acest document se intenţionează a fi un ghid “complet” pentru programarea cu ncurses şi librăriile adiacente. Vom începe cu un simplu program "Hello World" şi vom continua cu programe mai complexe. Nu este necesară experienţă anterioară cu ncurses.

Despre programe

Toate programele din acest document pot fi găsite în formă zip pe acest site. Unzip şi untar. Structura directoarelor arată astfel.

ncurses
   |
   |----> JustForFun   -- just for fun programs
   |----> basics               -- basic programs
   |----> demo         -- output files go into this directory after make
   |          |
   |          |----> exe       -- exe files of all example programs
   |          |----> obj       -- object files of all example programs
   |----> forms        -- programs related to form library
   |----> menus        -- programs related to menus library
   |----> panels               -- programs related to panels library
   |----> Makefile             -- the top level Makefile
   |----> README               -- the top level README file. contains instructions
   |----> COPYING              -- copyright notice
   |----> NCURSES_HOWTO.html -- this file 

Directoarele individuale arată astfel.

Descrierea fişierelor din fiecare direcor
--------------------------------------
JustForFun
    |
    |----> hanoi.c     -- The Towers of Hanoi Solver
    |----> life.c      -- The Game of Life demo
    |----> magic.c     -- An Odd Order Magic Square builder 
    |----> queens.c    -- The famous N-Queens Solver
    |----> shuffle.c   -- A fun game, if you have time to kill
    |----> tt.c        -- A very trivial typing tutor
 
  basics
    |
    |----> hello_world.c       -- Simple "Hello World" Program
    |----> init_func_example.c -- Initialization functions example
    |----> key_code.c          -- Shows the scan code of the key pressed
    |----> mouse_menu.c               -- A menu accessible by mouse
    |----> other_border.c      -- Shows usage of other border functions apart
    |                          -- box()
    |----> printw_example.c    -- A very simple printw() example
    |----> scanw_example.c     -- A very simple getstr() example
    |----> simple_attr.c       -- A program that can print a c file with comments
    |                          -- in attribute
    |----> simple_color.c      -- A simple example demonstrating colors
    |----> simple_key.c               -- A menu accessible with keyboard UP, DOWN arrows
    |----> temp_leave.c               -- Demonstrates temporarily leaving curses mode
    |----> win_border.c               -- Shows Creation of windows and borders
    |----> with_chgat.c               -- chgat() usage example
 
  forms 
    |
    |----> form_attrib.c       -- Usage of field attributes
    |----> form_options.c      -- Usage of field options
    |----> form_simple.c       -- A simple form example
    |----> form_win.c          -- Demo of windows associated with forms
 
  menus 
    |
    |----> menu_attrib.c       -- Usage of menu attributes
    |----> menu_item_data.c    -- Usage of item_name() etc.. functions
    |----> menu_multi_column.c -- Creates multi columnar menus
    |----> menu_scroll.c       -- Demonstrates scrolling capability of menus
    |----> menu_simple.c       -- A simple menu accessed by arrow keys
    |----> menu_toggle.c       -- Creates multi valued menus and explains
    |                          -- REQ_TOGGLE_ITEM
    |----> menu_userptr.c      -- Usage of user pointer
    |----> menu_win.c          -- Demo of windows associated with menus
 
  panels 
    |
    |----> panel_browse.c      -- Panel browsing through tab. Usage of user pointer
    |----> panel_hide.c               -- Hiding and Un hiding of panels
    |----> panel_resize.c      -- Moving and resizing of panels
    |----> panel_simple.c      -- A simple panel example

Există un fişier Makefile inclus în directorul principal. Construieşte toate fişierele şi pune fişierele executabile în directorul demo/exe. Le puteţi construe separate dând make în directorul specificat. Fiecare director conţine un fişier README care explică rolul fiecărui fişier c din director.

Pentru fiecare exemplu am dat calea spre fişier relative la directorul ncurses.

Toate programele sunt eliberate sub licenţa GPL şi puteţi să le folosţi pentru orice doriţi.

 

Credite

Îi mulţumesc lui Sharath şi lui Emre Akbas pentru că m-au ajutat la câteva secţiuni. Introducerea a fost scrisă iniţial de sharath. Am rescris-o folosind câteva secţiuni din introducerea scrisă de el. Emre a ajutat scriind secţiunile printw şi scanw.

Şi apoi urmează dragul meu priten Ravi Parimi care a participat la acest proiect chiar înainte de a scrie vreo linie. M-a stresat tot timpul cu idei şi a fost foarte răbdător corectând textul. De asemeni el a testat fiecare program în inux şi în Solaris. Vedeţi notele lui pentru problemele dumneavoastră.

Lista de dorinţe

Aceasta este lista de dorinţe în ordinea priorităţii. Dacă aveţi o dorinţă sau vreţi să participaţi la terminarea unei dorinţe trimiteţi-mi un mail la aici.

  • Adăugarea de exemple pentru ultima parte a secţiunii despre formulare. (Lucrez la acest lucru)
  • Crearea unui Demo ce arată toate programele şi lasă userul să navigheze prin descrierea fiecărui program. Lasă userul să compileze şi să vadă programul rulând. O interfaţă pe bază de dialoguri e preferată. (Prietenul meu N.N.Ashok lucrează la asta)
  • Adăugarea de informaţii de debug: _tracef, _tracemouse.
  • Accesul la termcap, terminfo folosind funcţii din pachetul ncurses.
  • Lucrul la două terminale simultan.
  • Adăugarea de lucruri noi în secţiunea diverse.

Lista de modificări

Versiunea 1.3.2

  • Ştergerea tuturor referinţelor la site-ul homestead 
  • Fişiere Makefile corectate

Versiunea 1.3.1

  • Am schimbat detial în detail
  • Am corectat paragraful despre administratori
  • Am corectat numărul versiunii stabile

Versiunea 1.3

  • Am adăugat în documentu principal copyright notice(LDP license).
  • Am pus copyright notice (GPL) pentru toate programele.
  • Am corectat exeplul despre printw.

Versiunea 1.2

  • Am incorporat schimbările lui ravi. În principal la secţiunile introducere, meniu, formulare, justforfun.
  • Câteva schimbări minore ca să arate bine în IE.

Versiunea 1.1

  • Am adăugat "un cuvât despre secţiunea ferestre".
  • Am corectat o mulţime de greşeli de scriere.
  • Am adăugat exemplul despre scanw.

Copyright

Copyright (c) 2001 de Pradeep Padala. Acest document poate fi distribuit sub licenţa LDP la linuxdoc.org/COPYRIGHT.html.

Acest HOWTO este un document gratuit; puteţi să-l modificaţi şi/sau redistribui respectând termenii licenţei LDP. Acest document e distribuit în speranţa că va fi util, dar fără nici o garanţie; chiar fără garanţia  comerciabilităţii sau folosirii pentru un scop specific. Vedeţi licenţa LDP pentru mai multe detalii.

Programul Hello World

Bine aţi venit în lumea curses. Înainte să pătrundem în librărie şi să vedem diversele facilităţi, să scriem un progrămel care să spună Hello lumii întregi.

Compilarea cu librăria Ncurses

Pentru a folosi funcţiile librării ncurses, trebuie să includeţi ncurses.h şi să legaţi programul cu librăria ncurses, flag-ul -lncurses trebuie adăugat. ncurses.h deja include stdio.h.

        #include <ncurses.h>
        .
        .
        .
 
        compile and link: gcc <program file> -lncurses

Codul

Programul Hello World !!!

/* File path: basics/hello_world.c */
#include <ncurses.h>
 
int main()
{       
        initscr();                     /* Start curses mode               */
        printw("Hello World !!!");     /* Print Hello World               */
        refresh();                     /* Refresh it on to the real screen */
        endwin();                      /* End curses mode                 */
 
        return 0;
}

Disecarea

Programul de mai sus afişează "Hello World !!!" pe ecran şi se termină. Acest program arată cum să iniţializăm curses şi să modificăm ecranul şi în final să ieşim din modul curses. Să-l urmărim lini cu linie.

Despre initscr()

Funcţia initscr() initializează terminalul în modul curses. În unele implementări şterge totul de pe ecran şi arată un ecran gol. Pentru a modifica ecranul folosind pachetul curses acesta trebuie întâi să fie apelat. Această funcţie iniţializează sistemul curses şi alocă memorie pentru fereastra actuală care se numeşte 'stdscr' şi alte structuri de date. În condiţii extreme această funcţie poate returna eroare din cauza memoriei insuficiente pentru a fi alocată pentru structurile de date.

După ce se realizează acest lucru putem face diferite iniţializări pentru a schimba setările sistemului ncurses. Aceste detalii vor fi explicate mai târziu.

Misteriosul refresh()

Linia printw afişează "Hello World !!!" pe ecran. Această funcţie e la fel ca printf în toate privinţele doar că afişează datele într-o fereastră numită stdscr la coordonatele curente (y,x). Deaorece coordonatele noastre sunt la 0,0 şirul e afişat în colţul din stânga sus al ferestrei..

Aşa am ajuns la misteriosul refresh(). Ei bine, când am apelat printw datele sunt scrise într-o fereastră imaginară numită stdscr, care nu este încă afişată pe ecran. Treaba lui printw este să schimbe câteva flag-uri şi structuri de date şi să scrie datele într-un buffer corespunzător lui stdscr. Pentru a o adduce pe ecran trebuie să apelăm refresh() şi să-I spună sistemului curses să şteargă conţinutul ecranului.

Filozofia din spatele tuturor acestora este să îi dea dreptul programatorului să facă updaturi multiple asupra ecranului sau ferestrelor şi să facă un refresh când toate update-urile sunt făcute.

refresh() verifică feerastra şi face modificări numai asupra părţilor care au fost modificate. Acest mod oferă un răspuns mai bun şi de asemeni o flexibilitate mai mare. Dar poate deveni frustant pentru începători. O greşeală des întâlnită la începători este să uite să apeleze refresh() după ce au făcut modificări cu funcţiile din clasa printw(). Chiar eu uit să o apelez uneori :-)

Despre endwin()

Şi în final nu uitaţi să terminaţi modul ncurses().Altfel s-ar putea să apară erori după ce se iese din el. endwin() eliberează memoria ocupată de sub-sistemul curses şi structurile lui de date şi trece terminalul în modul normal. Această funcţie trebuie apelată după ce aţi terminat cu modul ncurses.

Crudul detaliu

Acum că am văzut cum se scrie un simplu program ncurses să trecem la detalii. Sunt multe funcţii care ne ajută să customizăm ceea ce vedem pe ecran şi multe alte lucruri care pot fi exploatate la maxim.

Să începem...

Iniţializarea

Ştim că pentru a iniţializa sistemul curses trebuie să apelăm funcţia initscr(). Sunt funcţii care pot fi apelate după iniţializare pentru a customiza sistemul curses. Putem cere sitemului curses să seteze terminalul în mod raw sau să iniţializeze culorile sau să iniţializeze mouseul etc.. Să descutăm despre câteva funcţii care se apelează după apelarea lui initscr();

Despre funcţiile de iniţializare ca raw() etc...

raw() şi cbreak()

În mod normal driverul terminalului pune într-un buffer caracterele pe care utilizatorul le introduce de la tastatură până când se întâlneşte o linie nouă sau carriage return. Dar majoritatea programelor cer caracterele chiar când sunt tipărite. Cele două funcţii de mai sus sunt folosite pentru a dezactiva line buffering. Diferenţa dintre aceste două funcţii este în modul în care caracterele de control ca suspend (CTRL-Z), interrupt şi quit (CTRL-C) sunt trimise programului. În modul raw() aceste caractere sunt trimise programului fără a genera un semanl. În modul cbreak()aceste caractere de control sunt interpretate ca şi orice alt character de driverul terminalului. Eu prefer să folosesc raw() deoarece pot să controlez mai bine acţiunile utilizatorului.

echo() şi noecho()

Aceste funcţii controlează modul de apariţie a caracterelor introduses de utilizator la terminal. noecho() nu mai afişează caractere. Motivul pentru care aţi vrea să faceţi acest lucru este să aveţi mai mult control asupra echo-ului sau pentru a nu afişa anumite caractere în timp ce le citiţi de la utilizator prin intermediul funcţiilor getch()etc. Majoritatea programelor interactive apelează noecho() la iniţializare şi realizează afişarea caracterelor într-o manieră controlată. Îi dă posibilitatea programatorului să afişeze caratere oriunde pe fereastră fără a updata coordonatele (y,x).

keypad()

Aceasta e funcţia mea preferată de iniţializare. Permite citirea tastelor funcţionale ca F1, F2, săgeţi etc. Aproape orice program interactive activează această funcţie, deoarece săgeţile sunt o parte importantă din orice Interfaţă. Apelaţi keypad(stdscr, TRUE) pentru a activa această funcţie pentru ecranul standard(stdscr). Veţi învăţa mai multe despre managmentul tastelor mai târziu în acest document.

halfdelay()

Această funcţie deşi nu este folosită pre des poate fi foarte utilă. halfdelay() e apelată pentru a active modul half-delay, care este similar cu cbreak() pentru că caracterele tipărite sunt preluate de program chiar atunci când sunt tipărite. Totuşi, aşteaptă 'X' zecimi de secundă inputul şi apoi returnează ERR, dacă nu există input. 'X' este parametrul transmis funcţiei halfdelay(). Această funcţie este utilă atunci cănd se cere inputul de la utilizator, şi dacă nu răspunde în timp util, putem face altceva. Un posibil exemplu este la introducerea parolei.

Alte funcţii de iniţializare

Mai sunt câteva funcţii care sunt apelate la iniţializare pentru a modifica comporatmentul sistemului curses. Acestea nu sunt folosite la fel de des ca cele de mai sus şi le vom explica când le vom întâlni.

Un Exemplu

Să scrie un program care va clarifica utilizarea acestor funcţii.

The initialization functions example Program

/* File path: basics/init_func_example.c */
#include <ncurses.h>
 
int main()
{       int ch;
 
        initscr();                     /* Start curses mode           */
        raw();                         /* Line buffering disabled     */
        keypad(stdscr, TRUE);          /* We get F1, F2 etc..         */
        noecho();                      /* Don't echo() while we do getch */
 
        ch = getch();                  /* If raw() hadn't been called
                                       * we have to press enter before it
                                       * gets to the program                */
        if(ch == KEY_F(1))             /* Without keypad enabled this will */
               printw("F1 Key pressed");/*  not get to us either     */
                                      /* Without noecho() some ugly escape
                                       * charachters might have been printed
                                       * on screen                   */
        else
        {       printw("The pressed key is ");
               attron(A_BOLD);
               printw("%c", ch);
               attroff(A_BOLD);
        }
        refresh();                     /* Print it on to the real screen */
        endwin();                      /* End curses mode               */
 
        return 0;
}

Acest program este uşor de înţeles. Dar s-au folosit câteva funcţii care nu au fost încă prezentate. Funcţia getch() este folosită pentru a lua un caracter de la utilizator . Este echivalentă cu getchar() doar că putem dezactiva line buffering pentru a evita <enter>  din final. Căutaţi mai multe informaţii despre getch()şi tastele de citire în Secţiune depsre managmentul tastelor. Funcţiile attron şi attroff sunt folosite să activeze sau să dezactiveze anumite proprietăţi. În exemplu le-am folosit pentru a tipări textul îngroşat. Aceste funcţii vor fi explicate în detaliu mai târziu.

Un cuvânt despre ferestre

Înainte de a începe explicarea funcţiilor ncurses, să vă explic câteva lucruri despre ferestre. Ferestrele sunt explicate în detaliu în secţiunea următoare.

O fereastră este un ecran imginar definit de sistemul ncurses. O fereastră nu înseamnă o fereastră cu margini cum vedeţi în Win9x. Când curses este iniţializat, creează o fereastră iniţială numită stdscr care reprezintă ecranul dumneavoastră 80x25 (sau mărimea ecranului în care lucraţi). Dacă faceţi lucrui simple ca afişarea câtorva stringuri, să citim caractere etc.,puteţi folosi această fereastră fără probleme. Puteţi de asemeni creea ferestre şi apela funcţii specifice acelor ferestre.

De exemplu, dacă apelaţi

        printw("Hi There !!!");
        refresh();

afişează şirul pe stdscr la  poziţia curentă a cursorului. La fel şi apelul lui refresh(), merge numai pe stdscr.

Să spunem că aţi creat ferestre apoi trebuie să apelaţi toate funcţiile cu un 'w' în faţă.

        wprintw(win, "Hi There !!!");
        wrefresh(win);

După cum veţi vedea în restul documentului, denumirea funcţiilor urmează acelaşi standard. Pentru fiecare funcţie sunt în mod normal încă trei.

        printw(string);        /* Print on stdscr at present cursor position */
        mvprintw(y, x, string); /* Move to (y, x) then print string   */
        wprintw(win, string);  /* Print on windown win at present cursor position */
                               /* in the window */
        mvwprintw(win, y, x, string);  /* Move to (y, x) relative to window */
                                      /* co-ordinates and then print      */

De obicei funcţiile făra w sunt macrouri care extind funcţiile cu wpentru fereastra stdscr.

Despre funcţiile de ieşire ca printw()

Cred că de abia aşteptaţi să tercem la treabă. Din nou la odisea noastră despre funcţiile ncurses. Acum că curses este iniţializat, să interacţionăm cu lumea.

Sunt trei calse de funcţii care fac posibil afişarea pe ecran.

1.      clasa addch() : Afişarea unui singur caracter cu atribute

2.      clasa printw(): afişarea de text formatat la fel ca printf()

3.      clasa addstr(): afişarea de şiruri

Aceste funcţii pot fi folosite în acelaşi program şi este doar o problemă de stil în alegerea unei funcţii. Să ne uităm la fiecare în parte.

Clasa de funcţii addch()

Aceste funcţii pune un singur caracter  la poziţia curentă a cursorului şi mută cursorul. Puteţi da caracterul care vreţi să fie tipărit dar de obicei e folosită să fie tipărit un caracter cu atribute. Atributele sunt explicate în detaliu în secţiunea următoare. Dacă un character este asociat cu un atribut(bold, reverse video etc.), când curses afişează caracterul, acesta este afişat cu acel atribut.

Pentru a combina un caracter cu un atribut, aveţi opţiunile:

·         folosind OR pentru un singur caracter şi atributele dorite. Aceste macrouri de attribute pot fi găsite în fişierul ncurses.h. De exemplu, vreţi să afişaţi un caracter ch(de tip char) bold şi underlined. Veţi apela addch() ca ami jos.

     addch(ch | A_BOLD | A_UNDERLINE);

·         folosind funcţii ca attrset(),attron(),attroff(). Aceste funcţii sun t explicate în secţiunea Atribute. Pe scurt, ele schimbă atributele ferestrei curente. Odată setat, caraterul afişat în fereastră este asociat cu atributele până când  fereastra este închisă.

În plus, curses oferă şi câteva caractere speciale pentru grafica bazată pe caractere. Puteţi afişa tebele, linii verticale sau orizontale, etc. Puteţi găsi toate aceste caractere în fişierul ncurses.h. Căutaţi macrouri care încep cu ACS_ .

mvaddch(),waddch() şi mvwaddch()

mvaddch() este folosit pentru a muta cursorul la un anumit punct, şi apoi să-l afişeze. De aceea, apelurile:

        move(row,col); /* moves the cursor to rowth row and colth column */
        addch(ch);

poate fi înlocuit cu

        mvaddch(row,col,ch);

waddch() este similar cu addch(), numai că adaugă un caracter în fereastra dată. (Atenţie că addch() adaugă caracterul în fereastra stdscr.)

Într-un mod similar funcţia mvwaddch() este folosită pentru a adăuga un caracter în fereastra dată la coordonatele date.

Acum sunteţi familiar cu funcţia addch(). Dar, dacă doriţi să afişaţi un şir, ar fi foarte enervate să afişaţi character cu caracter. Din fericire, ncurses oferă funcţii ca printf sau puts.

clasa de funcţii printw()

Aceste funcţii sunt similare cu printf() cu posibilitatea de a scrie oriunde pe ecran.

printw() şi mvprintw

Aceste funcţii lucrează în mare ca şi printf(). mvprintw() poate fi folosit pentru a muta cursorul la o anumită poziţie şi apoi să afişăm. Dacă vreţi să mutaţi întâi cursorul şi apoi să afişaţi utilizând funcţia printw(), folosiţi întâi move() şi apoi printw() darn u văd nici un motiv pentru a evita folosirea lui mvprintw().

wprintw() şi mvwprintw

Aceste două funcţii sunt similare cu cele de mai sus cu observaţia că afişează în fereastra dată ca parametru.

vwprintw()

Această funcţie e similară cu vprintf(). Această funcţie e folosită când avem un număr variabil de argumente pentru tipărire.

 

Câteva exemple

/* File path: basics/printw_example.c */
#include <ncurses.h>                  /* ncurses.h includes stdio.h */  
#include <string.h> 
 
int main()
{
 char mesg[]="Just a string";         /* message to be appeared on the screen */
 int row,col;                         /* to store the number of rows and *
                                       * the number of colums of the screen */
 initscr();                           /* start the curses mode */
 getmaxyx(stdscr,row,col);            /* get the number of rows and columns */
 mvprintw(row/2,(col-strlen(mesg))/2,"%s",mesg);
                                      /* print the message at the center of the screen */
 mvprintw(row-2,0,"This screen has %d rows and %d columns\n",row,col);
 printw("Try resizing your window(if possible) and then run this program again");
 refresh();
 getch();
 endwin();
 
 return 0;
}           

Programul de mai sus exemplifică cât de uşor e de utilizat printw. Daţi doar coordonatele şi mesajul care trebuie să apară pe ecran, şi face tot.

Programul de mai sus introduce o nouă funcţie getmaxyx(), un macrou definit în ncurses.h. Returnează numărul de coloane şi numărul de rânduri dintr-o fereastră. getmaxyx() face acest lucru modificând variabila dată ca parametru. Deoarece getmaxyx() nu este o funcţie nu dăm pointeri ca argumente, dăm doar două variabile de tip int.

clasa de funcţii addstr()

addstr() este folosită pentru a pune un şir de caractere într-o fereastră. Această funcţie e similară cu addch() dată pentru fiecare caracter din şir. Aceasta este adevărată pentru toate funcţiile de afişare. Există şi alte funcţii din această familie: mvaddstr(), mvwaddstr() şi waddstr(), care corespund convenţiei de nume din ncurses.(e.g. mvaddstr() este similară cu apelarea lui move() şi apoi a lui addstr().) O altă funcţie din această familie este addnstr(), care ia un întreg ca parametru adiţional(de exemplu n). Această funcţie afişează cel mult n caractere. Dacă n este negativ, tot şirul va fi afişat.

Un cuvânt de advertisment

Toate aceste funcţii iau întâi coordonata y şi apoi x ca argumente. O greşeală comună pentru începători este să trimită coordonatele în ordinea x,y. Dacă faceţi pre multe operaţii cu coodonatele (y,x), gândiţivă să împărţiţi ecranul în ferestre şi să faceţi operaţii asupra fiecărei din ele separat. Ferestrele sunt explicate în secţiunea ferestre .

Despre funcţiile de intrare ca scanw()

Ei bine, afişarea fără citire este plictisitoare. Să vedem funcţiile care citesc de la utilizator. Aceste funcţii pot fi, şi ele, împărţite în trei categorii.

  1. clasa getch(): citeşte un caracter
  2. clasa scanw(): citeşte formatat
  3. clasa getstr(): citeşte şiruri

clasa de funcţii getch()

Aceste funcţii citesc un sigur character de la terminal. Dar sunt câteva lucruri de luat în seamă. De exemplu dacă nu folosiţi funcţia cbreak(), curses nu va citi caraterele în mod continuu ci va începe să le citească numai după o linie nouă sau EOF. Pentru a evita acest lucru, trebuie folosită funcţia cbreak() pentru ca, caracterele să fie accesibile imediat pentru citire. O altă funcţie des folosită este noecho(). După cum numele arată,când această funcţie e folosită, caarcterele care sunt testate de utilizator nu vor fi afişate pe ecran. Cele două funcţii cbreak() şi noecho() sunt exemple clasice de management al tastelor. Funcţii de acelaşi gen sunt explicate în  secţiunea de management al tastelor .

clasa de funcţii scanw()

Aceste funcţii sunt similare cu scanf() cu capabilitatea de a citi de pe ecran.

scanw() şi mvscanw

Folosirea acestor funcţii este similară lui sscanf(), unde linia de scanat este dată de funcţia wgetstr(). Asta e, aceste funcţii apelează wgetstr() şi foloseşte linia returnată pentru citire.

wscanw() şi mvwscanw()

Aceste funcţii sunt similare ca cele două de mai sus cu excepţia că citesc dintr-o fereastră, care este dată ca argument.

vwscanw()

This function is similar to vscanf(). This can be used when a variable number of arguments are to be scanned.

clasa de funcţii getstr()

Aceste funcţii sun folosite pentru a citi un şir de caractere de la terminal. În esenţă, această funcţie face acelaşi lucru ca o serie de apeluri a funcţiei getch() până se întâlneşte linie nouă, carriage return, sau sfârşit de fişier. Şirul de caractere rezultat va putea fi accesat prin str, care este un pointer la char dat de utilizator.

Nişte exemple

/* File path: basics/scanw_example.c */
#include <ncurses.h>                  /* ncurses.h includes stdio.h */  
#include <string.h> 
 
int main()
{
 char mesg[]="Enter a string: ";              /* message to be appeared on the screen */
 char str[80];
 int row,col;                         /* to store the number of rows and *
                                       * the number of colums of the screen */
 initscr();                           /* start the curses mode */
 getmaxyx(stdscr,row,col);            /* get the number of rows and columns */
 mvprintw(row/2,(col-strlen(mesg))/2,"%s",mesg);
                               /* print the message at the center of the screen */
 getstr(str);
 mvprintw(23, 0, "You Entered: %s", str);
 getch();
 endwin();
 
 return 0;
}

Atribute

Am văzut un exemplu cum pot fi folosite atributele pentru a afişa caractere cu anumite efecte speciale. Atributele, când sunt setate corect, pot prezenta informaţia într-o manieră uşor de înţeles. Următorul program ia un fişier C ca intrare şi afişează comentariile îngroşate. Scanează prin cod.

Exemplu

/* File path: basics/simple_attr.c */
#include <ncurses.h>
 
int main(int argc, char *argv[])
{       int ch, prev;
        FILE *fp;
        int goto_prev = FALSE, y, x;
 
        if(argc != 2)
        {       printf("Usage: %s <a c file name>\n", argv[0]);
               exit(1);
        }
        fp = fopen(argv[1], "r");
        if(fp == NULL)
        {       perror("Cannot open input file");
               exit(1);
        }
 
        initscr();                     /* Start curses mode           */
 
        prev = EOF;
        while((ch = fgetc(fp)) != EOF)
        {       if(prev == '/' && ch == '*')   /* If it is / and * then olny 
                                               * switch bold on */   
               {       attron(A_BOLD);
                       goto_prev = TRUE;      /* Go to previous char / and
                                               * print it in BOLD */
               }
               if(goto_prev == TRUE)
               {       getyx(stdscr, y, x);
                       move(y, x - 1);
                       printw("%c%c", '/', ch); /* The actual printing is done
                                                * here */
                       goto_prev = FALSE;     /* Set it to FALSE or every 
                                               * thing from here will be / */
               }
               else
                       printw("%c", ch);
               refresh();
               if(prev == '*' && ch == '/')
                       attroff(A_BOLD);       /* Switch it off once we got * 
                                                 and then / */
               prev = ch;
        }
        
        endwin();                      /* End curses mode               */
 
        return 0;
}

Nu vă faceţi griji pentru iniţializare şi pentru celelalte lucruri. Concetraţivă asupra buclei while. Citeşte fiecare character din fişier şi caută caracterele /*. Odată ce le-a găsit, activează atributul BOLD cu attron() . Când găsim */ e dezactivat cu funcţia attoff().

Programul de mai sus introduce şi alte funcţii foarte folositoare ca getyx() şi move(). Prima funcţie ia coordonatele y, x ale cursorului. Deoarece getyx() este un macrou nu trebuie să transmitem pointeri ca argumente. Funcţia move() mută cursorul la coordonatele date.

Programul de mai sus este unul simplu care nu face prea mult. Puteţi să scrieţi un program mai util care citeşte conţinutul unui fişier C parsează conţinutul şi îl afişează colorat. Puteţi să-l extindenţi chiar şi pentru alte limbaje.

Detalii

Să aflăm mai multe detalii despre atribute. Funcţiile attron(), attroff(), attrset()  şi attr_get() etc.. pot fi folosite pentru a active/dezactiva , a lua atributele şi a produce un ecran colorat.

Funcţiile attron şi attroff primesc o mască de biţi de attribute şi le activează sau le dezactivează. Următorul atribute video, care sunt definite în <curses.h> pot fi transmise acestor funcţii.

        
        A_NORMAL        Display normal (fără luminare)
        A_STANDOUT      Cel mai bu mod de iluminare pentru terminal.
        A_UNDERLINE     Subliniere
        A_REVERSE       Video invers
        A_BLINK         Clipire
        A_DIM           Luminat la jumătate
        A_BOLD          Extra luminat sau îngroşat
        A_PROTECT       Mod protejat
        A_INVIS         Mod invizibil sau blank
        A_ALTCHARSET    Set de caractere alternativ
        A_CHARTEXT      Masca de biţi pentru a extrage un caracter   COLOR_PAIR(n)   Perechea de culori numărul n    

Ultimul este cel mai plin de culoare :-) Culorile sunt explicate în secţiunea următoare.

Putem folosi OR(|) pentru orice număr de attribute din cele de mai sus pentru a obţine un effect combinat. Dacă vreţi video invers cu iluminare puteţi folosi

        attron(A_REVERSE | A_BLINK);

attron() vs attrset()

Atunci care e diferenţa dintre attron() şi attrset()? attrset setează atributele unei ferestre pe când attron doar activează atributul primit ca parametru. Aşadar attrset() suprascrie toate atributele ferestrei activându-le pe cele noi. La fel attroff() dezactivează atributele primate ca parametri. Astfel putem modifica foarte uşor atributele. Dar dacă le folosiţi inpropriu puteţi pierde socoteala atributelor unei ferestre şi ecranul se poate strica. Acest lucru se poate întâmpla mai des atunci când se folosesc meniuri colorate cu iluminare. Aşa că hotărâţi o politică consistentă şi urmaţi-o. Puteţi folosi oricând standend() care este echivalentă cu attrset(A_NORMAL) care dezactivează toate atributele şi le setează modul normal.

attr_get()

Funcţia attr_get() ia atributele şi perechea de culori a ferstrei curente. Deşi nu o puteţi folosi pe aceasta tot timpul, este foarte utilă pentru scanarea unor porţiuni din ecran. Să spunem că aţi vrut să faceţi nişte modificări asupra ecranului şi nu sunteţi sigur ce atribute are fiecare caracter. Atunci această funcţie poate fi folosită cu attrset sau attron pentru a rezulta efectul dorit.

funcţiile attr_

Sunt funcţii ca attr_set(), attr_on etc.. Aceste funcţii sunt la fel ca cele de mai sus doar că iau parametric de tip attr_t

funcţiile wattr

Pentru fieacre din funcţiile de mai sus există o variantă cu 'w' care acţionează asupra unei anumite ferestre. Funcţiile de mai sus acţionează asupra lui stdscr.

funcţiile chgat()

Funcţia chgat() e afişată în ultima parte a paginii man curs_attr. E chiar foarte folositoare. Această funcţie e folosită pentru a seta atributele pentru un grup de caractere fără a face mutări. Am vrut să spun !!! fără a muta cursorul :-) Schimbă atributele unui număr dat de caractere pornind de la poziţia actuală a cursorului.

Putem da -1 ca număr de caractere şi va modifica până la şfârşitul linie. Dacă vreţi să schimbaţi atributele caracterelor de la poziţia cfurentă până la sfârşitul linie, folosiţi:

        chgat(-1, A_REVERSE, 0, NULL);

Această funcţie este utilă atunci când schimbăm atributele la caraterele care sunt afişate. Mutaţivă la caracterul de unde vreţi să schimbaţi şi apelaţi funcţia.

Alte funcţii wchgat(), mvchgat(), wchgat() se comportă la fel doar că funcţiile cu w operează asupra unei anumite ferestre. Funcţia cu mv întâi mută cursorul apoi realizează acţiune dată ei. De fapt chgat este un macrou care este înlocuit cu wchgat() şi stdscr ca fereastră. Majoritatea funcţiilor fără "w" sunt macrouri.

Exemplu cu chgat()

/* File path: basics/with_chgat.c */
#include <ncurses.h>
 
int main(int argc, char *argv[])
{       initscr();                     /* Start curses mode           */
        start_color();                 /* Start color functionality   */
        
        init_pair(1, COLOR_CYAN, COLOR_BLACK);
        printw("A Big string which i didn't care to type fully ");
        mvchgat(0, 0, -1, A_BLINK, 1, NULL);  
        /* 
         * First two parameters specify the position at which to start 
         * Third parameter number of characters to update. -1 means till 
         * end of line
         * Forth parameter is the normal attribute you wanted to give 
         * to the charcter
         * Fifth is the color index. It is the index given during init_pair()
         * use 0 if you don't want color.
         * Sixth one is always NULL 
         */
        refresh();
        endwin();                      /* End curses mode               */
        return 0;
}

Acest exemplu ne introduce în culorile din curses. Culorile vor fi explicate în detaliu mai târziu. Folosiţi 0 pentru nici o culoare.

Totul despre funcţiile despre ferestre

Ferestrele formează cel mai important concept din curses. Aţi văzut fereastra standard stdscr mai sus. Acum pentru a face un design cât de simplu, trebuie să folosiţi ferestre. Motivul principal în folosirea ferestrelor este acela că se pot modifica părţi separate din ecran, pentru o mai bună eficienţă, modificând doar acele ferestre care au nevoie de modificări şi pentru un design mai bun. Aş spune că ultimul motiv e cel mai important pentru a folosi ferestre. Tot timpul trebuie să încercaţi să aveţi o interfaţă cât mai atractivă. Dacă scrieţi GUIs mari şi complexe ce urmează este foarte important.

Fundamente

O fereastră poate fi creată folosind funcţia newwin(). Nu creeză nimic pe ecarn ci alocă memorie pentru o structură pentru a face operaţii cu fereastra. Structura conţine date cum ar fi mărimea, coordonata y de început, coordonata x de început etc.. Deci în curses, o ferestră este doar o abstracţiune a unei ferestre imaginare, care poate fi modificată independent de celelalte părţi ale ecranului. Funcţia newwin() returnează un pointer la structura WINDOW, care poate fi transmisă unei funcţii ce se referă la ferestre ca wprintw() etc.. În sfârşit ferestra poate fi distrusă utilizând delwin(). Va dealoca memoria asociată cu structura ferestrei.

Să fie o Fereastră!!!

Unde-I farmecul dacă putem crea o ferestră darn u o putem vedea. Aşadar partea distractivă începe cu afişarea ferestrei. Funcţia box() poate fi folosită pentru a desena marginile în jurul unei ferestre. Haideţi să ne uităm mai în detaliu la aceste funcţii în exemplul următor.

Exemplu de margini

/* File path: basics/win_border.c */
#include <ncurses.h>
 
WINDOW *create_newwin(int height, int width, int starty, int startx);
void destroy_win(WINDOW *local_win);
 
int main(int argc, char *argv[])
{       WINDOW *my_win;
        int startx, starty, width, height;
        int ch;
 
        initscr();                     /* Start curses mode           */
        cbreak();                      /* Line buffering disabled, Pass on
                                       * everty thing to me          */
        keypad(stdscr, TRUE);          /* I need that nifty F1        */
 
        height = 3;
        width = 10;
        starty = (LINES - height) / 2; /* Calculating for a center placement */
        startx = (COLS - width) / 2;   /* of the window               */
        printw("Press F1 to exit");
        refresh();
        my_win = create_newwin(height, width, starty, startx);
 
        while((ch = getch()) != KEY_F(1))
        {       switch(ch)
               {       case KEY_LEFT:
                               destroy_win(my_win);
                               my_win = create_newwin(height, width, starty,--startx);
                               break;
                       case KEY_RIGHT:
                               destroy_win(my_win);
                              my_win = create_newwin(height, width, starty,++startx);
                               break;
                       case KEY_UP:
                               destroy_win(my_win);
                               my_win = create_newwin(height, width, --starty,startx);
                               break;
                       case KEY_DOWN:
                               destroy_win(my_win);
                               my_win = create_newwin(height, width, ++starty,startx);
                               break;  
               }
        }
               
        endwin();                      /* End curses mode               */
        return 0;
}
 
WINDOW *create_newwin(int height, int width, int starty, int startx)
{       WINDOW *local_win;
 
        local_win = newwin(height, width, starty, startx);
        box(local_win, 0 , 0);         /* 0, 0 gives default characters 
                                       * for the vertical and horizontal
                                       * lines                       */
        wrefresh(local_win);           /* Show that box               */
 
        return local_win;
}
 
void destroy_win(WINDOW *local_win)
{       
        /* box(local_win, ' ', ' '); : This won't produce the desired
         * result of erasing the window. It will leave it's four corners 
         * and so an ugly remnant of window. 
         */
        wborder(local_win, ' ', ' ', ' ',' ',' ',' ',' ',' ');
        /* The parameters taken are 
         * 1. win: the window on which to operate
         * 2. ls: character to be used for the left side of the window 
         * 3. rs: character to be used for the right side of the window 
         * 4. ts: character to be used for the top side of the window 
         * 5. bs: character to be used for the bottom side of the window 
         * 6. tl: character to be used for the top left corner of the window 
         * 7. tr: character to be used for the top right corner of the window 
         * 8. bl: character to be used for the bottom left corner of the window 
         * 9. br: character to be used for the bottom right corner of the window
         */
        wrefresh(local_win);
        delwin(local_win);
}

Explicarea

Nu plângeţi. Ştiu că este un exemplu mare. Dar am de explicat nişte lucruri importante :-). Acest program creează o fereastră dreptunghiulară care poate fi mutată cu tastele left, right, up, down. Creează şi distruge ferestre atunci când utilizatorul apasă pe taste. Nu ieşiţi din limitele ecranului. Verificarea acestor limite vă rămâne ca exerciţiu. Să îl disecăm linie cu linie.

Funcţia create_newwin() creează o fereastră cu newwin() şi afişează nişte margini în jur cu box(). Funcţia destroy_win() şterge întâi fereastra de pe ecran afişând o margine formată din caracterul ' ' şi apoi apelând  delwin() se dealocă memoria ce îi corespunde. În funcţie de tasta pe care o apasă utilizatorul, starty sau startx sunt schimbate şi se afişează o nouă fereastră.

În destroy_win, după cum vedeţi, am folosit wborder în loc de box. Motivul e specificat în comentarii (Aţi trecu peste. Ştiu. Citiţi codul :-)). wborder desenează un chenar în jurul ferestrei cu caracterele date ca paramatri ca cele 4 colţuri şi patru linii 4 lines. Să fiu mai bine înţeles, dacă aţi apelat wborder ca mai jos:

        wborder(win, '|', '|', '-', '-', '+', '+', '+', '+');


veţi vedea

        +-----------------------+
        |                      |
        |                      |
        |                      |
        |                      |
        |                      |
        |                      |
        +-----------------------+

Celelalte lucruri din exemplu

Puteţi vedea în exemplu că am folosit variabilele COLS, LINES care sunt iniţializate cu dimensiunile ecranului după initscr(). Sunt foarte utile în luare dimensiunilor ecranului şi aflarea centrului ecranului ca mai sus. Funcţia getch() ca de obicei ia caracterul de la tastatură şi în funcţie de caracter face ce trebuie. Acest tip de switch- case este foarte des utilizat în programe de interfaţă.

 

Alte funcţii de contur

Programul de mai sus este foarte inefficient deoarece la fiecare apăsare a unei taste o fereastră este distrusă şi alt este creată. Deci haideţi să creeăm un program mai bun care foloseşte funcţii de contur.

Programul următor foloseşte mvhline() şi mvvline() pentru a produce un effect similar. Aceste două funcţii sunt simple. Ele creează o linie verticală sau orizontală de dimensiunea dată la poziţia dată.

Codul

/* File path: basics/other_border.c */
#include <ncurses.h>
 
typedef struct _win_border_struct {
        chtype  ls, rs, ts, bs, 
                tl, tr, bl, br;
}WIN_BORDER;
 
typedef struct _WIN_struct {
 
        int startx, starty;
        int height, width;
        WIN_BORDER border;
}WIN;
 
void init_win_params(WIN *p_win);
void print_win_params(WIN *p_win);
void create_box(WIN *win, int bool);
 
int main(int argc, char *argv[])
{       WIN win;
        int ch;
 
        initscr();                     /* Start curses mode           */
        start_color();                 /* Start the color functionality */
        cbreak();                      /* Line buffering disabled, Pass on
                                       * everty thing to me          */
        keypad(stdscr, TRUE);          /* I need that nifty F1        */
        noecho();
        init_pair(1, COLOR_CYAN, COLOR_BLACK);
 
        /* Initialize the window parameters */
        init_win_params(&win);
        print_win_params(&win);
 
        attron(COLOR_PAIR(1));
        printw("Press F1 to exit");
        refresh();
        attroff(COLOR_PAIR(1));
        
        create_box(&win, TRUE);
        while((ch = getch()) != KEY_F(1))
        {       switch(ch)
               {       case KEY_LEFT:
                               create_box(&win, FALSE);
                               --win.startx;
                               create_box(&win, TRUE);
                               break;
                       case KEY_RIGHT:
                               create_box(&win, FALSE);
                               ++win.startx;
                               create_box(&win, TRUE);
                               break;
                       case KEY_UP:
                               create_box(&win, FALSE);
                               --win.starty;
                               create_box(&win, TRUE);
                               break;
                       case KEY_DOWN:
                               create_box(&win, FALSE);
                               ++win.starty;
                               create_box(&win, TRUE);
                               break;  
               }
        }
        endwin();                      /* End curses mode               */
        return 0;
}
void init_win_params(WIN *p_win)
{
        p_win->height = 3;
        p_win->width = 10;
        p_win->starty = (LINES - p_win->height)/2;    
        p_win->startx = (COLS - p_win->width)/2;
 
        p_win->border.ls = '|';
        p_win->border.rs = '|';
        p_win->border.ts = '-';
        p_win->border.bs = '-';
        p_win->border.tl = '+';
        p_win->border.tr = '+';
        p_win->border.bl = '+';
        p_win->border.br = '+';
 
}
void print_win_params(WIN *p_win)
{
#ifdef _DEBUG
        mvprintw(25, 0, "%d %d %d %d", p_win->startx, p_win->starty, 
                               p_win->width, p_win->height);
        refresh();
#endif
}
void create_box(WIN *p_win, int bool)
{       int i, j;
        int x, y, w, h;
 
        x = p_win->startx;
        y = p_win->starty;
        w = p_win->width;
        h = p_win->height;
 
        if(bool == TRUE)
        {       mvaddch(y, x, p_win->border.tl);
               mvaddch(y, x + w, p_win->border.tr);
               mvaddch(y + h, x, p_win->border.bl);
               mvaddch(y + h, x + w, p_win->border.br);
               mvhline(y, x + 1, p_win->border.ts, w - 1);
               mvhline(y + h, x + 1, p_win->border.bs, w - 1);
               mvvline(y + 1, x, p_win->border.ls, h - 1);
               mvvline(y + 1, x + w, p_win->border.rs, h - 1);
 
        }
        else
               for(j = y; j <= y + h; ++j)
                       for(i = x; i <= x + w; ++i)
                               mvaddch(j, i, ' ');
                               
        refresh();
 
}

Totul despre culori

Viaţa e plictisitoare fără culori. Sistemul Curses are un mechanism drăguţ pentru a lucra cu culori. Haideţi să le vedem într-un exemplu simplu.

Un example simplu

/* File path: basics/simple_color.c */
#include <ncurses.h>
 
void print_in_middle(WINDOW *win, int starty, int startx, int width, char *string);
int main(int argc, char *argv[])
{       initscr();                     /* Start curses mode           */
        if(has_colors() == FALSE)
        {       endwin();
               printf("You terminal does not support color\n");
               exit(1);
        }
        start_color();                 /* Start color                 */
        init_pair(1, COLOR_RED, COLOR_BLACK);
 
        attron(COLOR_PAIR(1));
        print_in_middle(stdscr, LINES / 2, 0, 0, "Viola !!! In color ...");
        attroff(COLOR_PAIR(1));
        endwin();
}
void print_in_middle(WINDOW *win, int starty, int startx, int width, char *string)
{       int length, x, y;
        float temp;
 
        if(win == NULL)
               win = stdscr;
        getyx(win, y, x);
        if(startx != 0)
               x = startx;
        if(starty != 0)
               y = starty;
        if(width == 0)
               width = 80;
 
        length = strlen(string);
        temp = (width - length)/ 2;
        x = startx + (int)temp;
        mvwprintw(win, y, x, "%s", string);
        refresh();
}

După cum vedeţi, pentru a începe să folosiţi culori trebuie să apelaţi funcţia start_color(). După aceea puteţi folosi capabilităţile de culori ale terminalului utilizând diverse funcţii. Pentru a afla care terminal are capabilităţi de culori, puteţi folosi funcţia has_colors(), care returnează FALSE dacă terminalul nu suportă culori.

Sistemul Curses iniţializează toate culorile suportate de terminal când este apelată start_color(). Acestea pot fi accesate prin constante ca COLOR_BLACK etc. Acum pentru a folosi culori trebuie să definiţi perechi. Culorile sunt întotdeauna utilizate în perechi. Aceasta înseamnă că trebuie să folosiţi funcţia init_pair() pentru a defini fundalul şi culoarea de fond pentru perechea pe care o daţi. După aceasta acea pereche de culori poate fi utilizată cu funcţia COLOR_PAIR(). Poate părea oarecum dificil la început. Dar această soluţie simplă ne oferă un mod foarte uşor de a folosi perechile de culori. Pentru a o aprecia, trebuie să vă uitaţi în codul sursă al "dialog-ului",un utilitar care vă arată boxe de dialog dintr-un shell script. Dezvoltatorii au definit combinaţii de culori de fundal şi de fond pentru taote culorile de care au nevoie şi le iniţializează la început. Astfel devine foarte uşor de setat atributele doar accesând o pereche pe care deja am definit-o ca o constantă.

Următoarele culori sunt definite în curses.h. Puteţi folosi aceşti parametri ca diferite funcţii de culoare.

               COLOR_BLACK   0
               COLOR_RED     1
               COLOR_GREEN   2
               COLOR_YELLOW  3
               COLOR_BLUE    4
               COLOR_MAGENTA 5
               COLOR_CYAN    6
               COLOR_WHITE   7

Schimbarea definiţiilor culorilor

Funcţia init_color()poate fi folosită pentru a schimba valorile rgb ale culorilor definite iniţial de curses. Să zicem că vreţi să măriţi intensitatea culorii roşu cu o unitate. Puteţi folosi funcţia

        init_color(COLOR_RED, 700, 0, 0);
        /* param 1      : color name
         * param 2, 3, 4 : rgb content min = 0, max = 1000 */

Dacă terminalul dumneavoastră nu poate schimba definiţiile culorilor, funcţia returnează ERR. Funcţia can_change_color() poate fi folosită pentru a vedea dacă terminalul are capabilitatea de a schimba conţinutul culorilor sau nu. Conţinutul rgb este scalat de la 0 la 1000. Iniţial culoare ROŞU este definită cu conţinutul 1000(r), 0(g), 0(b).

Conţinutul Culorii

Funcţiile color_content() şi pair_content() pot fi folosite pentru a obţine conţinutul culorii şi combinaţiile de fond şi fundal a perechii.

Managmentul tastelor. Cum să citiţi tastele funcţionale, tastele săgeţi etc..

Fundamente

Nici un GUI nu e complet fără o interfaţă puternică cu utilizatorul, un program curses ar trebui să răspundă la tastele apăsate sau la acţiunile mouse-ului. Să ne ocupăm întâi de taste.

După cum aţi văzut în majoritatea exemplelor de mai sus, e foarte uşor să citiţi caractere de la tastatură. O mod uşor de a lua tasta apăsată este să folosiţi funcţia getch(). The Modul cbreak ar trebui activate pentru a citi câte un caracter şi nu un şir de caracter (care se termină de obicei cu carriage return). Keypad-ul ar trbui activate pentru a citi taste funcţionale, săgeţi etc. Vedeţi secţiunea de iniţializare pentru mai multe detalii.

getch() returnează un integer ce reprezintă tasta apăsată. Dacă e un character normal, valoarea returnată e echivalentă cu, caracterul. Altfel returnează un număr care poate fi verificat cu o constantă din curses.h. De exemplu dacă utilizatorul apasă F1, întregul returnat va fi  265. acesta poate fi verificat utilizând macroul KEY_F() definit în curses.h. Aceasta face cititul tastelor portabil şi uşor de folosit.

De exemplu, dacă apelaţi getch() astfel

        int ch;
 
        ch = getch();

getch() va aştepta ca utilizatorul să apese o tastă, (doar dacă nu specificaţi un timeout) şi când utilizatorul apasă o tastă, întregul corespunzător este returnat. Apoi puteţi verifica valoarea returnată cu constanteledefinite în curses.h pentru tastele pe care le vreţi.

Următoarea bucată de cod face acest lucru.

        if(ch == KEY_LEFT)
               printw("Left arrow is pressed\n");

Să scriem un mic program care creează un meniu care poate fi navigat în sus şi în jos de săgeţi.

Codul

/* File path: basics/simple_key.c */
#include <stdio.h>
#include <ncurses.h>
 
#define WIDTH 30
#define HEIGHT 10 
 
int startx = 0;
int starty = 0;
 
char *choices[] = { 
                       "Choice 1",
                       "Choice 2",
                       "Choice 3",
                       "Choice 4",
                       "Exit",
                 };
int n_choices = sizeof(choices) / sizeof(char *);
void print_menu(WINDOW *menu_win, int highlight);
 
int main()
{       WINDOW *menu_win;
        int highlight = 1;
        int choice = 0;
        int c;
 
        initscr();
        clear();
        noecho();
        cbreak();      /* Line buffering disabled. pass on everything */
        startx = (80 - WIDTH) / 2;
        starty = (24 - HEIGHT) / 2;
               
        menu_win = newwin(HEIGHT, WIDTH, starty, startx);
        keypad(menu_win, TRUE);
        mvprintw(0, 0, "Use arrow keys to go up and down, Press enter to select a choice");
        refresh();
        print_menu(menu_win, highlight);
        while(1)
        {       c = wgetch(menu_win);
               switch(c)
               {       case KEY_UP:
                               if(highlight == 1)
                                      highlight = n_choices;
                               else
                                      --highlight;
                               break;
                       case KEY_DOWN:
                               if(highlight == n_choices)
                                      highlight = 1;
                               else 
                                      ++highlight;
                               break;
                       case 10:
                               choice = highlight;
                               break;
                       default:
                               mvprintw(24, 0, "Charcter pressed is = %3d Hopefully it can be printed as '%c'", c, c);
                               refresh();
                               break;
               }
               print_menu(menu_win, highlight);
               if(choice != 0) /* User did a choice come out of the infinite loop */
                       break;
        }       
        mvprintw(23, 0, "You chose choice %d with choice string %s\n", choice, choices[choice - 1]);
        clrtoeol();
        refresh();
        endwin();
        return 0;
}
 
 
void print_menu(WINDOW *menu_win, int highlight)
{
        int x, y, i;   
 
        x = 2;
        y = 2;
        box(menu_win, 0, 0);
        for(i = 0; i < n_choices; ++i)
        {       if(highlight == i + 1) /* High light the present choice */
               {       wattron(menu_win, A_REVERSE); 
                       mvwprintw(menu_win, y, x, "%s", choices[i]);
                       wattroff(menu_win, A_REVERSE);
               }
               else
                       mvwprintw(menu_win, y, x, "%s", choices[i]);
               ++y;
        }
        wrefresh(menu_win);
}

Folosirea mouse-ului

Acum că aţi văzut cum se citesc tastele, să facem acelaşi lucru pentru mouse. De obicei fiecare UI îi permite utilizatorului să acţioneze asupra ei atât cu tastatura cât şi cu mouse-ul.

Fundamente

Înainte de a face altceva, evenimentele pe care vreţi să le primiţi trebuie să fie activate cu mousemask().

        mousemask(mmask_t newmask,     /* The events you want to listen to */
                 mmask_t *oldmask)    /* The old events mask             */

Primul parametru din funcţia de mai sus este o mască de biţi a evenimentelor pe care vreţi să le ascultaţi. La început toate evenimentele sunt dezactivate. Masca de biţi ALL_MOUSE_EVENTS poate fi folosită pentru a active toate evenimentele.

În continuare vă prezint toate evenimentele cu măştile corespunzătoare:

        Nume                   Descriere
       ---------------------------------------------------------------------
       BUTTON1_PRESSED          mouse button 1 down
       BUTTON1_RELEASED         mouse button 1 up
       BUTTON1_CLICKED          mouse button 1 clicked
       BUTTON1_DOUBLE_CLICKED   mouse button 1 double clicked
       BUTTON1_TRIPLE_CLICKED   mouse button 1 triple clicked
       BUTTON2_PRESSED          mouse button 2 down
       BUTTON2_RELEASED         mouse button 2 up
       BUTTON2_CLICKED          mouse button 2 clicked
       BUTTON2_DOUBLE_CLICKED   mouse button 2 double clicked
       BUTTON2_TRIPLE_CLICKED   mouse button 2 triple clicked
       BUTTON3_PRESSED          mouse button 3 down
       BUTTON3_RELEASED         mouse button 3 up
       BUTTON3_CLICKED          mouse button 3 clicked
       BUTTON3_DOUBLE_CLICKED   mouse button 3 double clicked
       BUTTON3_TRIPLE_CLICKED   mouse button 3 triple clicked
       BUTTON4_PRESSED          mouse button 4 down
       BUTTON4_RELEASED         mouse button 4 up
       BUTTON4_CLICKED          mouse button 4 clicked
       BUTTON4_DOUBLE_CLICKED   mouse button 4 double clicked
       BUTTON4_TRIPLE_CLICKED   mouse button 4 triple clicked
       BUTTON_SHIFT             shift was down during button state change
       BUTTON_CTRL              control was down during button state change
       BUTTON_ALT               alt was down during button state change
       ALL_MOUSE_EVENTS         report all button state changes
       REPORT_MOUSE_POSITION    report mouse movement

Obţinerea evenimentelor

Odată ce o clasă de evenimente corespunzătoare mouse-ului a fost activată, clasa de funcţii getch() returnează KEY_MOUSE de fiecare dată când se petrece un eveniment al mouseului. Apoi evenimentul poate fi citit cu getmouse().

Codul arată cam astfel:

        MEVENT event;
 
        ch = getch();
        if(ch == KEY_MOUSE)
               if(getmouse(&event) == OK)
                       .       /* Do some thing with the event */
                       .
                       .

getmouse() returnează evenimentul în pointerul care i se dă. E o structură care conţine

        typedef struct
        {
               short id;         /* ID pentru a distinge între mai multe dispozitive */
               int x, y, z;      /* coordonatele evenimentului */
               mmask_t bstate;   /* biţii de stare a butonului */
        }    

bstate este variabila principală care ne interesează. Ne spune care este starea butonului mouselui.

Apoi cu o bucată de cod ca mai jos putem afla ce s-a întâmplat.

        if(event.bstate & BUTTON1_PRESSED)
               printw("Left Button Pressed");

Cam asta e tot în legătură cu mouseul. Să creeăm acelaşi meniu şi să activăm interacţiunea cu mouse-ul. Pentru a face lucrurile mai simple nu se mai ascultă tastele.

Exemplu despre interactţiunea cu Mouse-ul

/* File Path: basics/mouse_menu.c */
#include <ncurses.h>
 
#define WIDTH 30
#define HEIGHT 10 
 
int startx = 0;
int starty = 0;
 
char *choices[] = {    "Choice 1",
                       "Choice 2",
                       "Choice 3",
                       "Choice 4",
                       "Exit",
                 };
 
int n_choices = sizeof(choices) / sizeof(char *);
 
void print_menu(WINDOW *menu_win, int highlight);
void report_choice(int mouse_x, int mouse_y, int *p_choice);
 
int main()
{       int c, choice = 0;
        WINDOW *menu_win;
        MEVENT event;
 
        /* Initialize curses */
        initscr();
        clear();
        noecho();
        cbreak();      //Line buffering disabled. pass on everything
 
        /* Try to put the window in the middle of screen */
        startx = (80 - WIDTH) / 2;
        starty = (24 - HEIGHT) / 2;
        
        attron(A_REVERSE);
        mvprintw(23, 1, "Click on Exit to quit");
        refresh();
        attroff(A_REVERSE);
 
        /* Print the menu for the first time */
        menu_win = newwin(HEIGHT, WIDTH, starty, startx);
        print_menu(menu_win, 1);
        /* Get all the mouse events */
        mousemask(ALL_MOUSE_EVENTS, NULL);
        
        while(1)
        {       c = wgetch(menu_win);
               switch(c)
               {       case KEY_MOUSE:
                       if(getmouse(&event) == OK)
                       {       /* When the user clicks left mouse button */
                               if(event.bstate & BUTTON1_PRESSED)
                               {       report_choice(event.x + 1, event.y + 1, &choice);
                                      if(choice == -1) //Exit chosen
                                              goto end;
                                      mvprintw(22, 1, "Choice made is : %d String Chosen is \"%10s\"", choice, choices[choice - 1]);
                                      refresh(); 
                               }
                       }
                       print_menu(menu_win, choice);
                       break;
               }
        }              
end:
        endwin();
        return 0;
}
 
 
void print_menu(WINDOW *menu_win, int highlight)
{
        int x, y, i;   
 
        x = 2;
        y = 2;
        box(menu_win, 0, 0);
        for(i = 0; i < n_choices; ++i)
        {       if(highlight == i + 1)
               {       wattron(menu_win, A_REVERSE); 
                       mvwprintw(menu_win, y, x, "%s", choices[i]);
                       wattroff(menu_win, A_REVERSE);
               }
               else
                       mvwprintw(menu_win, y, x, "%s", choices[i]);
               ++y;
        }
        wrefresh(menu_win);
}
 
/* Report the choice according to mouse position */
void report_choice(int mouse_x, int mouse_y, int *p_choice)
{       int i,j, choice;
 
        i = startx + 2;
        j = starty + 3;
        
        for(choice = 0; choice < n_choices; ++choice)
               if(mouse_y == j + choice && mouse_x >= i && mouse_x <= i + strlen(choices[choice]))
               {       if(choice == n_choices - 1)
                               *p_choice = -1;        
                       else
                               *p_choice = choice + 1;        
                       break;
               }
}

Funcţii diverse

Funcţiile mouse_trafo() şi wmouse_trafo() sunt folosite pentru a converti coodonatele mouselui la coordinate relative a ecranului. Vedeţi pagina man a lui curs_mouse(3X).

Funcţia mouseinterval setează timpul maxim (în miimi de secundă) care poate trece între evenimentele de apăsare şi eliberare a unui buton aşa încât să fie recumoscut ca un click. Această funcţie returnează vechea valoare a intervalului. Normal este o cincime dintr-o secundă.

Manevrarea Ecranului

În această secţiune, ne vom uita la câteva funcţii, care ne ajută să manevrăm uşor ecranul şi să scriem programe cât mai bune. Acest lucru e important mai ales când se scriu jocuri.

funcţiile getyx()

Funcţia getyx() poate fi folosită pentru a afla coordonatele actuale ale cursorului. Va pune valorile coordonatelor x şi y în arargumentele ce le primeşte. Deoarece getyx() este un macrou nu trebuie să transmiteţi adresa variabilelor. Poate fi apelată astfel

        getyx(win, y, x);
        /* win: pointer la fereastră
         *   y, x: coordonatele y, x sunt puse în aceste variabile    */

Funcţia getparyx() ia coordonatele de început a unei sub ferestre relative la fereastra principală . Câteodată poate fi foarte util pentru a updata o sub fereastră. Atunci când faceţi lucruri complexe ca meniuri multiple, e dificil de  a păstra poziţia meniului, poziţia primei opţiuni etc. O soluţie simplă la această problemă este să creaţi meniuri în sub ferestre şi apoi să găsiţi coordonatle de start a sub ferestrei folosind getparyx().

Funcţiile  getbegyx() şi getmaxyx() salvează coordonatele de început şi de sfârşit a ferestrei actuale. Aceste funcţii sun la fel de utile ca cea mai de sus pentru a manevra ferestrele şi sub ferestrele în mod eficient.

Screen Dumping

În timp ce scrieţi ferestre, câteodată este nevoie să salvaţi starea ecranului şi apoi să îl încărcaţi în aceeaşi stare. Funcţia scr_dump() poate fi folosită pentru a salva conţinutul ecranului într-un fisier dat ca argument. Apoi poate fi încărcat cu funcţia scr_restore. Aceste două funcţii pot folosite eficient pentru a ne ocupa de un joc cu mai multe scenarii.

Window Dumping

Pentru a salva şi încărca ferestre pot fi folosite funcţiile putwin() şi getwin(). putwin() salvează starea curentă a unei stări, care poate fi încărcată mai târziu cu getwin().

Funcţia copywin() poate fi folosită pentru a copia o fereastră într-o altă fereastră. Ia ferestrele sursă şi destinaţie şi conform regiuni dreptunghiulare specificate, copie regiunea dreptunghiulară din sursă în destinaţie. Ultimul parametru specifică dacă să suprascriem sau să punem deasupra conţinutul în fereastra destinaţie. Dacă acest argument e true, atunci copierea e nedestructivă.

Funcţii diverse

Acum cunoaşteţi destule funcţii pentru a scrie un program curses destul de bun. Mai există şi alte funcţii care pot fi foarte utile în anumite momente. Haideţi să vedem câteva dintre ele.

curs_set()

Această funcţie e folosită pentru a ascunde cursorul. Parametrii acestei funcţii sunt:

        0 : invisibil  sau
        1 : normal     sau
        2 : foarte vizibil.

Părăsirea temporară a modului Curses

Câteodată veţi vrea să vă întoarceţi la modul normal. În astfel de cazuri veţi trebui să salvaţi modurile tty cu funcţia def_prog_mode() şi apoi să apelaţi endwin() pentru a termina modul curses. Astfel veţi intra în modul tty standard. Pentru a vă reîntoarce în modul curses apelaţi reset_prog_mode(). Această funcţie returnează modul tty a stării salvate cu def_prog_mode() . Apoi faceţi refresh(), şi sunteţi din nou în modul curses. Iată un exemplu al paşilor care trebuie urmaţi.

/* File Path: basics/temp_leave.c */
#include <ncurses.h>
 
int main()
{       
        initscr();                     /* Start curses mode             */
        printw("Hello World !!!\n");   /* Print Hello World             */
        refresh();                     /* Print it on to the real screen */
        def_prog_mode();               /* Save the tty modes            */
        endwin();                      /* End curses mode temporarily   */
        system("/bin/sh");             /* Do whatever you like in cooked mode */
        reset_prog_mode();             /* Return to the previous tty mode*/
                                      /* stored by def_prog_mode()     */
        refresh();                     /* Do refresh() to restore the          */
                                      /* Screen contents               */
        printw("Another String\n");    /* Back to curses use the full    */
        refresh();                     /* capabilities of curses        */
        endwin();                      /* End curses mode               */
 
        return 0;
}

Alte librării

Pe lângă librăria curses mai există şi câteva librării, care furnizează mai multă funcţionalitate şi o mulţime de facilităţi. Această secţiune explică trei librării care vin odată cu ncurses.

Librăria Panel

Acum că vă descurcaţi în curses vreţi să faceţi un program mare. Aţi creat o mulţime de ferestre suprapuse pentru a da un effect de windows. Din păcate devine greu să le manipulăm. Toate modficările ca refreshes şi updates devin un coşmar. Ferestrele suprapuse pot creea erori dacă nu sunt reafişate în mod corect.

Nu disperaţi. Există o soluţie elegantă oferită de librăria Panel. Să cităm dezvoltatorii lui ncurses

When your interface design is such that windows may dive deeper into the visibility stack or pop to the top at runtime, the resulting book-keeping can be tedious and difficult to get right. Hence the panels library.

Dacă aveţi multe ferestre suprapuse atunci trebuie să folosiţi librăria Panel. NU mai trebuie să faceţi wnoutrefresh(), doupdate() pentru fiecare ferestră în parte şi să o faceţi în ordinea exactă. Librăria păstrează informaţii despre ordinea ferestrelor, a suprapunerii şi modifică ecranul corespunzător. Deci de ce să aşteptăm? Să ne uităm mai în detaliu.

Fundamente

Obiectul panel e considerat ca o fereastră care face parte din deck ce include toate celelalte obiecte panel. Deck-ul este tratat ca o stivă cu panelul de sus vizibil iar celelalte paneluri pot fi vizibile sau nu în funcţie de poziţia lor. Aşa că idea de bază este de a creea o stivă de paneluri şi de a le afişa corect. Există o funcţie similară cu refresh() care afişează panelurile în ordinea corectă. Sunt funcţii de a afişa, ascunde, muta paneluri, a schimba mărimea lor etc.. De problema suprapunerii  se ocupă librăria panel când se apelează aceste funcţii.

Ordinea generală a programului cu paneluri este următoarea:

  1. Creaţi ferestrele ce se ataşează la paneluri (cu newwin()).
  2. Crearea de paneluri cu ordinea de vizibilitatea de care aveţi nevoie. Le puneţi în stivă după cum vreţi să fie vizibile. Pentru a creea un panel folosiţi new_panel().
  3. Apelaţi update_panels() pentru a afişa panelurile pe ecranul virtual în ordinea corectă. Apelaţi doupdate() pentru a afişa pe ecran.
  4. Puteţi modifica panelurile cu show_panel(), hide_panel(), move_panel() etc. Folosiţi funcţiile ajutătoare panel_hidden() şi panel_window(). Folosţi pointeri pentru a stoca date într-un panel. Folosiţi funcţiile set_panel_userptr() şi panel_userptr() pentru a seta şi a obţine pointerul.
  5. Când aţi terminat cu panelul folosiţi del_panel() pentru a-l şterge.

Să exemplificăm conceptele cu câteva exemple. Următorul e un program simplu care creează 3 paneluri suprapuse şi le afişează pe ecran.

Compilarea cu librăria Panel

Pentru a folosi funcţiile din librăria panel, trebuie să includeţi panel.h şi să faceţi legătura cu librăria panel folosind -lpanel urmat apoi de -lncurses în ordinea specificată.

        #include <panel.h>
        .
        .
        .
 
        compilare şi legare: gcc <program file> -lpanel -lncurses

 

Un exemplu simplu

/* File Path: panels/panel_simple.c */
#include <panel.h>
 
int main()
{       WINDOW *my_wins[3];
        PANEL  *my_panels[3];
        int lines = 10, cols = 40, y = 2, x = 4, i;
 
        initscr();
        cbreak();
        noecho();
 
        /* Create windows for the panels */
        my_wins[0] = newwin(lines, cols, y, x);
        my_wins[1] = newwin(lines, cols, y + 1, x + 5);
        my_wins[2] = newwin(lines, cols, y + 2, x + 10);
 
        /* 
         * Create borders around the windows so that you can see the effect
         * of panels
         */
        for(i = 0; i < 3; +++i)
               box(my_wins[i], 0, 0);
 
        /* Attach a panel to each window */   /* Order is bottom up */
        my_panels[0] = new_panel(my_wins[0]);         /* Push 0, order: stdscr-0 */
        my_panels[1] = new_panel(my_wins[1]);         /* Push 1, order: stdscr-0-1 */
        my_panels[2] = new_panel(my_wins[2]);         /* Push 2, order: stdscr-0-1-2 */
 
        /* Update the stacking order. 2nd panel will be on top */
        update_panels();
 
        /* Show it on the screen */
        doupdate();
        
        getch();
        endwin();
}

După cum vedeţi, urmează un fir simplu după cum am explicat anterior. Ferestrele sunt create cu newwin() şi sunt ataşate de paneluri cu new_panel(). Stiva de paneluri e updatată după cum creeăm un panel după altul. Pentru a le afişa apelăm update_panels() şi doupdate().

Urmează un exemplu puţin mai complicat. Acest program creează 3 ferestre care pot fi schimbate una cu alta prin tab. Vedeţi codul.

Un exemplu de navigare prin ferestre

/* File Path: panels/panel_browse.c */
#include <panel.h>
 
#define NLINES 10
#define NCOLS 40
 
void init_wins(WINDOW **wins, int n);
void win_show(WINDOW *win, char *label, int label_color);
void print_in_middle(WINDOW *win, int starty, int startx, int width, char *string, chtype color);
 
int main()
{       WINDOW *my_wins[3];
        PANEL  *my_panels[3];
        PANEL  *top;
        int ch;
 
        /* Initialize curses */
        initscr();
        start_color();
        cbreak();
        noecho();
        keypad(stdscr, TRUE);
 
        /* Initialize all the colors */
        init_pair(1, COLOR_RED, COLOR_BLACK);
        init_pair(2, COLOR_GREEN, COLOR_BLACK);
        init_pair(3, COLOR_BLUE, COLOR_BLACK);
        init_pair(4, COLOR_CYAN, COLOR_BLACK);
 
        init_wins(my_wins, 3);
        
        /* Attach a panel to each window */   /* Order is bottom up */
        my_panels[0] = new_panel(my_wins[0]);         /* Push 0, order: stdscr-0 */
        my_panels[1] = new_panel(my_wins[1]);         /* Push 1, order: stdscr-0-1 */
        my_panels[2] = new_panel(my_wins[2]);         /* Push 2, order: stdscr-0-1-2 */
 
        /* Set up the user pointers to the next panel */
        set_panel_userptr(my_panels[0], my_panels[1]);
        set_panel_userptr(my_panels[1], my_panels[2]);
        set_panel_userptr(my_panels[2], my_panels[0]);
 
        /* Update the stacking order. 2nd panel will be on top */
        update_panels();
 
        /* Show it on the screen */
        attron(COLOR_PAIR(4));
        mvprintw(LINES - 2, 0, "Use tab to browse through the windows (F1 to Exit)");
        attroff(COLOR_PAIR(4));
        doupdate();
 
        top = my_panels[2]; /* Store the top panel pointer */
        while((ch = getch()) != KEY_F(1))
        {       switch(ch)
               {       case 9:
                               top = (PANEL *)panel_userptr(top); /* Find out the next panel in the cycle */
                               top_panel(top); /* Make it as the top panel */
                               break;
               }
               update_panels();
               doupdate();
        }
        endwin();
        return 0;
}
 
/* Put all the windows */
void init_wins(WINDOW **wins, int n)
{       int x, y, i;
        char label[80];
 
        y = 2;
        x = 10;
        for(i = 0; i < n; ++i)
        {       wins[i] = newwin(NLINES, NCOLS, y, x);
               sprintf(label, "Window Number %d", i + 1);
               win_show(wins[i], label, i + 1);
               y += 3;
               x += 7;
        }
}
 
/* Show the window with a border and a label */
void win_show(WINDOW *win, char *label, int label_color)
{       int startx, starty, height, width;
 
        getbegyx(win, starty, startx);
        getmaxyx(win, height, width);
 
        box(win, 0, 0);
        mvwaddch(win, 2, 0, ACS_LTEE); 
        mvwhline(win, 2, 1, ACS_HLINE, width - 2); 
        mvwaddch(win, 2, width - 1, ACS_RTEE); 
        
        print_in_middle(win, 1, 0, width, label, COLOR_PAIR(label_color));
}
 
void print_in_middle(WINDOW *win, int starty, int startx, int width, char *string, chtype color)
{       int length, x, y;
        float temp;
 
        if(win == NULL)
               win = stdscr;
        getyx(win, y, x);
        if(startx != 0)
               x = startx;
        if(starty != 0)
               y = starty;
        if(width == 0)
               width = 80;
 
        length = strlen(string);
        temp = (width - length)/ 2;
        x = startx + (int)temp;
        wattron(win, color);
        mvwprintw(win, y, x, "%s", string);
        wattroff(win, color);
        refresh();
}

Folosirea pointerilor utilizator

În exemplul de mai sus am folosit un pointer pentru a afla următoarea fereastră. Putem ataşa informaţii la o fereastră specificând un pointer, care poate pointa spre orice informaţie doriţi să stocaţi. În acest caz stochez pointerul spre ferestra următoare. Pointerii utilizator pentru un panel pot fi setaţi cu set_panel_userptr(). Poate fi accesat cu funcţia panel_userptr() care va returna pointerul utilizator pentru fereastra dată ca utilizator. După ce găsiţi următorul panel puteţi să-l aduceţi deasupra cu funcţia top_panel(). Această funcţie adduce panelul dat ca argument în vârful stivei de paneluri.

Mutarea şi redimensionarea panelurilor

Funcţia move_panel() poate fi folosită pentru a muta panelul la coordonatele dorite. NU modifică poziţia panelului în stivă. Fiţi siguri că folosiţi move_panel() în loc de mvwin() pentru fereastra ataşată panelului.

Redimensionarea unui panel e un pic mai complicată. Nu există o funcţie directă pentru a redimensiona fereasta ataşată panelului. O soluţie pentru a redimensiona un panel este de a creea o nouă fereastră cu dimensiunile dorite şi să schimbaţi dimensiunile ataşate panelului cu funcţia replace_panel(). Nu uitaţi să ştergeţi vechea fereastră. Fereastra ataşată panelului poate fi găsită cu funcţia panel_window().

Următorul program ilustrează aceste concepte. Puteţi schimba ferestrele, ca de obicei, cu <TAB>. Pentru a muta sau redimensiona panelul current apăsaţi 'r' pentru redimensionare şi 'm' pentru mutare. Apoi folosiţi săgeţile pentru a muta sau redimensiona fereastra după cum doriţi şi apăsaţi enter pentru a termina. Acest exemplu foloseşte date utilizator pentru a realize operaţiile dorite.

Exemplu de mutare şi redimensionare

/* File Path: panels/panel_resize.c */
#include <panel.h&ht;
 
typedef struct _PANEL_DATA {
        int x,  /* Startx */ 
        y,      /* Starty */
        w,      /* Width  */
        h;      /* Height */
        char label[80]; /* Label for the window */
        int label_color; /* Color number for the label */
        PANEL *next;   /* Pointer to the next Panel in the cycle */
}PANEL_DATA;
 
#define NLINES 10
#define NCOLS 40
 
void init_wins(WINDOW **wins, int n);
void win_show(WINDOW *win, char *label, int label_color);
void print_in_middle(WINDOW *win, int starty, int startx, int width, char *string, chtype color);
void set_user_ptrs(PANEL **panels, int n);
 
int main()
{       WINDOW *my_wins[3];
        PANEL  *my_panels[3];
        PANEL_DATA  *top;
        PANEL *stack_top;
        WINDOW *temp_win, *old_win;
        int ch;
        int newx, newy, neww, newh;
        int size = FALSE, move = FALSE;
 
        /* Initialize curses */
        initscr();
        start_color();
        cbreak();
        noecho();
        keypad(stdscr, TRUE);
 
        /* Initialize all the colors */
        init_pair(1, COLOR_RED, COLOR_BLACK);
        init_pair(2, COLOR_GREEN, COLOR_BLACK);
        init_pair(3, COLOR_BLUE, COLOR_BLACK);
        init_pair(4, COLOR_CYAN, COLOR_BLACK);
 
        init_wins(my_wins, 3);
        
        /* Attach a panel to each window */   /* Order is bottom up */
        my_panels[0] = new_panel(my_wins[0]);         /* Push 0, order: stdscr-0 */
        my_panels[1] = new_panel(my_wins[1]);         /* Push 1, order: stdscr-0-1 */
        my_panels[2] = new_panel(my_wins[2]);         /* Push 2, order: stdscr-0-1-2 */
 
        set_user_ptrs(my_panels, 3);
        /* Update the stacking order. 2nd panel will be on top */
        update_panels();
 
        /* Show it on the screen */
        attron(COLOR_PAIR(4));
        mvprintw(LINES - 2, 0, "Use tab to browse through the windows (F1 to Exit)");
        attroff(COLOR_PAIR(4));
        doupdate();
 
        stack_top = my_panels[2];
        top = (PANEL_DATA *)panel_userptr(stack_top);
        newx = top->x;
        newy = top->y;
        neww = top->w;
        newh = top->h;
        while((ch = getch()) != KEY_F(1))
        {       switch(ch)
               {       case 9:        /* Tab */
                               top = (PANEL_DATA *)panel_userptr(stack_top);
                               top_panel(top->next);
                               stack_top = top->next;
                               top = (PANEL_DATA *)panel_userptr(stack_top);
                               newx = top->x;
                               newy = top->y;
                               neww = top->w;
                               newh = top->h;
                               break;
                       case 'r':      /* Re-Size*/
                               size = TRUE;
                               attron(COLOR_PAIR(4));
                               mvprintw(LINES - 3, 0, "Entered Resizing :Use Arrow Keys to resize and press <ENTER> to end resizing");
                               refresh();
                               attroff(COLOR_PAIR(4));
                               break;
                       case 'm':      /* Move */
                               attron(COLOR_PAIR(4));
                               mvprintw(LINES - 3, 0, "Entered Moving: Use Arrow Keys to Move and press <ENTER> to end moving");
                               refresh();
                               attroff(COLOR_PAIR(4));
                               move = TRUE;
                               break;
                       case KEY_LEFT:
                               if(size == TRUE)
                               {       --newx;
                                      ++neww;
                               }
                               if(move == TRUE)
                                      --newx;
                               break;
                       case KEY_RIGHT:
                               if(size == TRUE)
                               {       ++newx;
                                      --neww;
                               }
                               if(move == TRUE)
                                      ++newx;
                               break;
                       case KEY_UP:
                               if(size == TRUE)
                               {       --newy;
                                      ++newh;
                               }
                               if(move == TRUE)
                                      --newy;
                               break;
                       case KEY_DOWN:
                               if(size == TRUE)
                               {       ++newy;
                                      --newh;
                               }
                               if(move == TRUE)
                                      ++newy;
                               break;
                       case 10:       /* Enter */
                               move(LINES - 3, 0);
                               clrtoeol();
                               refresh();
                               if(size == TRUE)
                               {       old_win = panel_window(stack_top);
                                      temp_win = newwin(newh, neww, newy, newx);
                                      replace_panel(stack_top, temp_win);
                                      win_show(temp_win, top->label, top->label_color); 
                                      delwin(old_win);
                                      size = FALSE;
                               }
                               if(move == TRUE)
                               {       move_panel(stack_top, newy, newx);
                                      move = FALSE;
                               }
                               break;
                       
               }
               update_panels();
               doupdate();
        }
        endwin();
        return 0;
}
 
/* Put all the windows */
void init_wins(WINDOW **wins, int n)
{       int x, y, i;
        char label[80];
 
        y = 2;
        x = 10;
        for(i = 0; i < n; ++i)
        {       wins[i] = newwin(NLINES, NCOLS, y, x);
               sprintf(label, "Window Number %d", i + 1);
               win_show(wins[i], label, i + 1);
               y += 3;
               x += 7;
        }
}
 
/* Set the PANEL_DATA structures for individual panels */
void set_user_ptrs(PANEL **panels, int n)
{       PANEL_DATA *ptrs;
        WINDOW *win;
        int x, y, w, h, i;
        char temp[80];
        
        ptrs = (PANEL_DATA *)calloc(n, sizeof(PANEL_DATA));
 
        for(i = 0;i < n; ++i)
        {       win = panel_window(panels[i]);
               getbegyx(win, y, x);
               getmaxyx(win, h, w);
               ptrs[i].x = x;
               ptrs[i].y = y;
               ptrs[i].w = w;
               ptrs[i].h = h;
               sprintf(temp, "Window Number %d", i + 1);
               strcpy(ptrs[i].label, temp);
               ptrs[i].label_color = i + 1;
               if(i + 1 == n)
                       ptrs[i].next = panels[0];
               else
                       ptrs[i].next = panels[i + 1];
               set_panel_userptr(panels[i], &ptrs[i]);
        }
}
 
/* Show the window with a border and a label */
void win_show(WINDOW *win, char *label, int label_color)
{       int startx, starty, height, width;
 
        getbegyx(win, starty, startx);
        getmaxyx(win, height, width);
 
        box(win, 0, 0);
        mvwaddch(win, 2, 0, ACS_LTEE); 
        mvwhline(win, 2, 1, ACS_HLINE, width - 2); 
        mvwaddch(win, 2, width - 1, ACS_RTEE); 
        
        print_in_middle(win, 1, 0, width, label, COLOR_PAIR(label_color));
}
 
void print_in_middle(WINDOW *win, int starty, int startx, int width, char *string, chtype color)
{       int length, x, y;
        float temp;
 
        if(win == NULL)
               win = stdscr;
        getyx(win, y, x);
        if(startx != 0)
               x = startx;
        if(starty != 0)
               y = starty;
        if(width == 0)
               width = 80;
 
        length = strlen(string);
        temp = (width - length)/ 2;
        x = startx + (int)temp;
        wattron(win, color);
        mvwprintw(win, y, x, "%s", string);
        wattroff(win, color);
        refresh();
}

Să ne concentrăm asupra buclei while. Odată găsită tasta apăsată realizează acţiunea cerută. Dacă este apăsat 'r' modul de redimensionare e pornit. După aceasta noile dimensiuni sunt introduce folosind tastele săgeţi. Când se apasă <ENTER> secţiunea de redimensionare se termină şi se face redimensionarea. Când sunteţi în modul redimesionare nu se arată cât se redimesionează fereastra. Rămâne ca exerciţiu afişarea unei margini punctate a ferestrei redimensionate.

Când utilizatorul apasă 'm' modulul de mutare e pornit. Acesta e un pic mai simplu decât redimesionarea. Când funcţiile săgeţi sunt apăsate se modifică poziţia ferestrei şi când se apasă <ENTER> se mută fereastra apelând funcţia move_panel().

În acest program datele utilizator sunt reprezentate ca PANEL_DATA şi e foarte importantă pentru a găsi informaţii despre panel. După cum e scris în comentarii, PANEL_DATA stochează dimensiunile, labelurile, culorile labelurilor şi un pointer la următoarea fereastră.

Ascunderea şi afişarea Panelurilor

Un panel poate fi ascuns folosind funcţia hide_panel(). Această funcţie nu face altceva decât să-l şteargă din stivă astfel ne mai apărând când apelaţi update_panels() şi doupdate(). Nu distruge structura PANEL asociată cu panelul ascuns. Poate fi afişat din nou cu funcţia show_panel().

Următorul program arată cum se ascunde un panel. Apăsaţi 'a' sau 'b' sau 'c' pentru a ascunde sau arăta prima, a doua sau a treia fereastră. Se foloseşte o variabilă utilizator care memorează dacă o fereastră e ascunsă sau nu. Dintr-un anumit motiv funcţia panel_hidden() care spune dacă un panel este ascuns sau nun u funcţionează. Un bug report a fost prezentat de Michael Andres aici

Exemplu cu ascunderea şi afişarea Panelurilor

/* File Path: panels/panel_hide.c */
#include <panel.h>
 
typedef struct _PANEL_DATA {
        int hide;      /* TRUE if panel is hidden */
}PANEL_DATA;
 
#define NLINES 10
#define NCOLS 40
 
void init_wins(WINDOW **wins, int n);
void win_show(WINDOW *win, char *label, int label_color);
void print_in_middle(WINDOW *win, int starty, int startx, int width, char *string, chtype color);
 
int main()
{       WINDOW *my_wins[3];
        PANEL  *my_panels[3];
        PANEL_DATA panel_datas[3];
        PANEL_DATA *temp;
        int ch;
 
        /* Initialize curses */
        initscr();
        start_color();
        cbreak();
        noecho();
        keypad(stdscr, TRUE);
 
        /* Initialize all the colors */
        init_pair(1, COLOR_RED, COLOR_BLACK);
        init_pair(2, COLOR_GREEN, COLOR_BLACK);
        init_pair(3, COLOR_BLUE, COLOR_BLACK);
        init_pair(4, COLOR_CYAN, COLOR_BLACK);
 
        init_wins(my_wins, 3);
        
        /* Attach a panel to each window */   /* Order is bottom up */
        my_panels[0] = new_panel(my_wins[0]);         /* Push 0, order: stdscr-0 */
        my_panels[1] = new_panel(my_wins[1]);         /* Push 1, order: stdscr-0-1 */
        my_panels[2] = new_panel(my_wins[2]);         /* Push 2, order: stdscr-0-1-2 */
 
        /* Initialize panel datas saying that nothing is hidden */
        panel_datas[0].hide = FALSE;
        panel_datas[1].hide = FALSE;
        panel_datas[2].hide = FALSE;
 
        set_panel_userptr(my_panels[0], &panel_datas[0]);
        set_panel_userptr(my_panels[1], &panel_datas[1]);
        set_panel_userptr(my_panels[2], &panel_datas[2]);
 
        /* Update the stacking order. 2nd panel will be on top */
        update_panels();
 
        /* Show it on the screen */
        attron(COLOR_PAIR(4));
        mvprintw(LINES - 3, 0, "Show or Hide a window with 'a'(first window)  'b'(Second Window)  'c'(Third Window)");
        mvprintw(LINES - 2, 0, "F1 to Exit");
 
        attroff(COLOR_PAIR(4));
        doupdate();
        
        while((ch = getch()) != KEY_F(1))
        {       switch(ch)
               {       case 'a':                      
                               temp = (PANEL_DATA *)panel_userptr(my_panels[0]);
                               if(temp->hide == FALSE)
                               {       hide_panel(my_panels[0]);
                                      temp->hide = TRUE;
                               }
                               else
                               {       show_panel(my_panels[0]);
                                      temp->hide = FALSE;
                               }
                               break;
                       case 'b':
                               temp = (PANEL_DATA *)panel_userptr(my_panels[1]);
                               if(temp->hide == FALSE)
                               {       hide_panel(my_panels[1]);
                                      temp->hide = TRUE;
                               }
                               else
                               {       show_panel(my_panels[1]);
                                      temp->hide = FALSE;
                               }
                               break;
                       case 'c':
                               temp = (PANEL_DATA *)panel_userptr(my_panels[2]);
                               if(temp->hide == FALSE)
                               {       hide_panel(my_panels[2]);
                                      temp->hide = TRUE;
                               }
                               else
                               {       show_panel(my_panels[2]);
                                      temp->hide = FALSE;
                               }
                               break;
               }
               update_panels();
               doupdate();
        }
        endwin();
        return 0;
}
 
/* Put all the windows */
void init_wins(WINDOW **wins, int n)
{       int x, y, i;
        char label[80];
 
        y = 2;
        x = 10;
        for(i = 0; i < n; ++i)
        {       wins[i] = newwin(NLINES, NCOLS, y, x);
               sprintf(label, "Window Number %d", i + 1);
               win_show(wins[i], label, i + 1);
               y += 3;
               x += 7;
        }
}
 
/* Show the window with a border and a label */
void win_show(WINDOW *win, char *label, int label_color)
{       int startx, starty, height, width;
 
        getbegyx(win, starty, startx);
        getmaxyx(win, height, width);
 
        box(win, 0, 0);
        mvwaddch(win, 2, 0, ACS_LTEE); 
        mvwhline(win, 2, 1, ACS_HLINE, width - 2); 
        mvwaddch(win, 2, width - 1, ACS_RTEE); 
        
        print_in_middle(win, 1, 0, width, label, COLOR_PAIR(label_color));
}
 
void print_in_middle(WINDOW *win, int starty, int startx, int width, char *string, chtype color)
{       int length, x, y;
        float temp;
 
        if(win == NULL)
               win = stdscr;
        getyx(win, y, x);
        if(startx != 0)
               x = startx;
        if(starty != 0)
               y = starty;
        if(width == 0)
               width = 80;
 
        length = strlen(string);
        temp = (width - length)/ 2;
        x = startx + (int)temp;
        wattron(win, color);
        mvwprintw(win, y, x, "%s", string);
        wattroff(win, color);
        refresh();
}

Funcţiile panel_above() şi panel_below()

Funcţiile panel_above() şi panel_below() pot fi folosite pentru a afla care este panelul de deasupra sau cel de dedesubt. Dacă argumentul este NULL, atunci ele returnează un pointer la fereastra de deasupra respective la cea de dedesubt.

Librăria Menu

Librăria menu oferă o extensie drăguţă pentru curses cu ajutorul căreia puteţi creea meniuri. Oferă un set de funcţii pentru a creea meniuri. Dar trebuie să fie customizate pentru a da un look mai interesant: culori etc. Să intrăm în detalii.

Un meniu este o colecţie de opţiuni din care utilizatorul poate allege una sau mai multe. Unii cititori nu cred că ştiu de opţiunea de a selecta mai multe opţiuni. Librăria menu oferă capabiliotatea de a creea meniuri din care utilizatorul poate selecta mai multe opţiuni. Ne vom ocupa de acest lucru în secţiunea următoare. Acum e timpul petru ceva mai simplu.

Fundamente

Pentru a creea meniuri, trebuie întâi să creaţi opţiuni şi apoi să afişaţi meniul. După aceea, toate răspunsurile utilizatorului sunt tratate într-o funcţie elegantă numită menu_driver() care reprezintă baza oricărui program cu meniuri.

Paşii care trebuie urmaţi pentru a creea un program cu meniuri sunt următorii.

  1. Iniţializează curses
  2. Crearea de opţiuni cu new_item(). Puteţi specifica un nume şi o descriere pentru opţiune.
  3. Creaţi meniul cu new_menu() specificând opţiunile ce le va conţine.
  4. Plasaţi meniul cu menu_post() şi eractualizaţi ecranul.
  5. Procesaţi acţiunile asupra meniului cu o buclă şi faceţi orice modificări necesare cu funcţia menu_driver.
  6. Ştergeţi meniul cu menu_unpost()
  7. Eliberaţi memoria alocată meniului cu free_menu()
  8. Eliberaţi memoria alocată opţiunilor cu free_item()
  9. Terminaţi curses

Să vedem un program simplu care afişează un meniu şi se modifică opţiunea selectată cu săgeţile up, down.

Compilarea cu librăria Menu

Pentru a folosi funcţiile din librăria menu trebuie să includeţi menu.h şi să faceţi legătura programului cu librăria menu folosind -lmenu lângă -lncurses în ordinea specificată.

        #include <menu.h>
        .
        .
        .
 
        compile and link: gcc <program file> -lmenu -lncurses

Un exemplu simplu

/* File Path: menus/menu_simple.c */
#include <menu.h>
 
#define ARRAY_SIZE(a) (sizeof(a) / sizeof(a[0]))
#define CTRLD  4
 
char *choices[] = {
                        "Choice 1",
                        "Choice 2",
                        "Choice 3",
                        "Choice 4",
                        "Exit",
                  };
 
int main()
{       ITEM **my_items;
        int c;                         
        MENU *my_menu;
        int n_choices, i;
        ITEM *cur_item;
        
        
        initscr();
        cbreak();
        noecho();
        keypad(stdscr, TRUE);
        
        n_choices = ARRAY_SIZE(choices);
        my_items = (ITEM **)calloc(n_choices + 1, sizeof(ITEM *));
 
        for(i = 0; i < n_choices; ++i)
                my_items[i] = new_item(choices[i], choices[i]);
        my_items[n_choices] = (ITEM *)NULL;
 
        my_menu = new_menu((ITEM **)my_items);
        post_menu(my_menu);
        refresh();
 
        while((c = getch()) != KEY_F(1))
        {       switch(c)
                {      case KEY_DOWN:
                               menu_driver(my_menu, REQ_DOWN_ITEM);
                               break;
                       case KEY_UP:
                               menu_driver(my_menu, REQ_UP_ITEM);
                               break;
               }
        }       
 
        free_item(my_items[0]);
        free_item(my_items[1]);
        free_menu(my_menu);
        endwin();
}

Acest program arată cum să creaţi un meniu simplu folosind librăria menu. Întâi creăm opţiunile cu new_item() şi apoi le ataşăm de meniu cu funcţia new_menu(). După ce plasăm şi afişăm meniul bucla de procesare e pornită. Citeşte inputul utilizatorului şi face acţiunea specificată. Funcţia menu_driver() este baza sistemului de meniuri. Al doilea paramentru al funcţiei spune ce trebuie făcut cu meniul. Corespunzător parametrului menu_driver() face acţiunea corespunzătoare. Valoarea poate fi o cerere de navigare în meniu, un caracter ascii, sau un caracter special asociat unui eveniment al mouselului KEY_MOUSE.

menu_driver acceptă următoarele cereri de navigare în meniu.

         REQ_LEFT_ITEM         Mută în stânga pe o opţiune.
         REQ_RIGHT_ITEM        Mută în dreapta pe o opţiune.
         REQ_UP_ITEM           Mută în sus pe o opţiune.
         REQ_DOWN_ITEM         Mută în jos pe o opţiune.
         REQ_SCR_ULINE         Scroll în sus cu o linie.
         REQ_SCR_DLINE          Scroll în jos cu o linie.
         REQ_SCR_DPAGE          Scroll în jos cu o pagină.
         REQ_SCR_UPAGE         Scroll în sus cu o pagină.
         REQ_FIRST_ITEM        Mutare pe prima opţiune.
         REQ_LAST_ITEM         Mutare pe ultima opţiune.
         REQ_NEXT_ITEM         Mutare pe următoarea opţiune.
         REQ_PREV_ITEM         Mutare pe opţiunea precedentă.
         REQ_TOGGLE_ITEM       Selectează/deselectează o opţiune.
         REQ_CLEAR_PATTERN     Şterge bufferul de pattern al meniului.
         REQ_BACK_PATTERN      Şterge caracterul precedent din bufferul de pattern al meniului
         REQ_NEXT_MATCH        Mutare pe următoarea opţiune ce e egală cu patternul.
         REQ_PREV_MATCH        Mutare pe opţiunea precedentă ce e egală cu patternul.
 

Nu fiţi intimidaţi de mulţimea de opţiuni. Le vom prezenta pe fiecare în parte. Opţiunile de interes pentru acest exemplu sunt REQ_UP_ITEM şi REQ_DOWN_ITEM. Aceste două opţiuni când sunt trimise lui menu_driver, driverul de meniu schimbă opţiunea curentă în joss au în sus.

Driverul de meniu: Baza sistemului de meniuri

După cum aţi văzut în exemplul de mai sus, menu_driver joacă un rol foarte important în afişarea meniului. Este foarte important să înţelegeţi parametrii care îi acceptă şi ce fac. După cum am explicat mai sus al doilea parametru al lui menu_driver() poate fi o cerere de navigare, un character printabil sau un caracter KEY_MOUSE. Să ne uităm peste cererile de navigare.

  • REQ_LEFT_ITEM şi REQ_RIGHT_ITEM

Un meniu poate fi afişat cu mai multe coloane pentru unul sau mai multe opţiuni. Acest lucru se poate face utilizând funcţia menu_format(). Când un meniu multi-coloană este afişat aceste cereri fac ca selecţia curentă se mută de la stânga la dreapta.

  • REQ_UP_ITEM şi REQ_DOWN_ITEM

Aceste două opţiuni le-aţi văzut în exemplul de mai sus. Când sunt date aceste opţiuni menu_driver mută selecţia curentă în sus sau în jos.

  • REQ_SCR_* options

Cele patru opţiuni REQ_SCR_ULINE, REQ_SCR_DLINE, REQ_SCR_DPAGE, REQ_SCR_UPAGE se referă la scrolling. Dacă nu se pot afişa toate opţiunile din meniu în fereastra meniului atunci meniul e scrolabil. Aceste cereri se pot transmite lui menu_driver pentru a face scrollingul cu o linie în joss au în sus, sau cu o pagina în jos au în sus.

  • REQ_FIRST_ITEM, REQ_LAST_ITEM, REQ_NEXT_ITEM şi REQ_PREV_ITEM

Aceste cereri se explică din denumire.

  • REQ_TOGGLE_ITEM

Această cerere deselectează selecţia curentă. Această opţiune poate fi folosită numai într-un meniu multivalue. Deci pentru a folosi această cerere O_ONEVALUE trebuie dezactivat. Această opţiune poate activată sau dezactivată cu set_menu_opts().

  • Cereri de Patern

Fiecare meniu are asociat un buffer de patern, care e folosit pentru a găsi cea mai apropiată potrivire cu caracterele ascii date de utilizator. Oricând caracterele ascii sunt transmise lui menu_driver, sunt puse în buffer de patern. Deasemeni încearcă să găsească cea mai apropiată potrivire cu paternal din lista de opţiuni şi mută selecţia curentă pe acea opţiune. Cererea REQ_CLEAR_PATTERN şterge buferul de patern. Cererea REQ_BACK_PATTERN şterge caracterul precedent din buferul de patern. În caz că se potrivesc mai multe opţiuni din meniu atunci se poate naviga prin acestea cu REQ_NEXT_MATCH şi REQ_PREV_MATCH care mută selecţia curentă la următoarea respectiv la anterioara opţiune ce se potriveşte căutării.

  • Cereri de la Mouse

În caz că se foloseşte KEY_MOUSE, se face o acţiune conform poziţiei mouseului. Acţiunea care se face e descrisă în paginile man astfel,

 
Dacă al doilea argument e caracterul special KEY_MOUSE evenimentul asocial al moseului e translatat în una din cererile de mai sus.   Acum numai clickurile în fereastra utilizator sunt tratate (e.g. în fereastra meniului sau în fereastra de decoraţii). Dacă faceţi click deasupra ferestrei meniului, se generează REQ_SCR_ULINE,  dacă faceţi dubluclick se generează  REQ_SCR_UPAGE  şi dacă faceţi tripluclick se generează REQ_FIRST_ITEM.  Dacă faceţi click dedesubtul ferestrei meniului, se generează REQ_SCR_DLINE, dacă faceţi dubluclick se generează  REQ_SCR_DPAGE is generated şi dacă faceţi tripluclick se generează REQ_LAST_ITEM. Dacă faceţi click pe o opţiune din meniu cursorul se pozţionează pe acea opţiune.
 

Fieacare din cererile de mai sus va fi explicată în exemplul de  mai jos.

Ferestre Meniu

Fiecare meniu care e creat este asociat cu o fereastră şi o subfereastră. Fereastra meniului afişează orice titlu sau margine asociată cu meniul. Subfereastra meniului conţine opţiunile meniului. Dar nu am specificat nici o fereastră sau subfereastră în exemplu. Când nu este specificată nici o ferestră, stdscr este luată ca fereastra principală, şi apoi sistemul meniu calculează dimensiunea subferestrei necesară pentru afişarea opţiunilor. Opţiunile sunt afişate în subfereastra respectivă. Deci haideţi să ne jucăm cu aceste ferestre şi să afişăm un meniu cu titlu şi margini.

Exemplu de Fereastră Meniu

/* File Path: menus/menu_win.c */
#include <menu.h>
 
#define ARRAY_SIZE(a) (sizeof(a) / sizeof(a[0]))
#define CTRLD  4
 
char *choices[] = {
                        "Choice 1",
                        "Choice 2",
                        "Choice 3",
                        "Choice 4",
                        "Exit",
                        (char *)NULL,
                  };
void print_in_middle(WINDOW *win, int starty, int startx, int width, char *string, chtype color);
 
int main()
{       ITEM **my_items;
        int c;                         
        MENU *my_menu;
        WINDOW *my_menu_win;
        int n_choices, i;
        
        /* Initialize curses */
        initscr();
        start_color();
        cbreak();
        noecho();
        keypad(stdscr, TRUE);
        init_pair(1, COLOR_RED, COLOR_BLACK);
 
        /* Create items */
        n_choices = ARRAY_SIZE(choices);
        my_items = (ITEM **)calloc(n_choices, sizeof(ITEM *));
        for(i = 0; i < n_choices; ++i)
                my_items[i] = new_item(choices[i], choices[i]);
 
        /* Crate menu */
        my_menu = new_menu((ITEM **)my_items);
 
        /* Create the window to be associated with the menu */
        my_menu_win = newwin(10, 40, 4, 4);
        keypad(my_menu_win, TRUE);
     
        /* Set main window and sub window */
        set_menu_win(my_menu, my_menu_win);
        set_menu_sub(my_menu, derwin(my_menu_win, 6, 38, 3, 1));
 
        /* Set menu mark to the string " * " */
        set_menu_mark(my_menu, " * ");
 
        /* Print a border around the main window and print a title */
        box(my_menu_win, 0, 0);
        print_in_middle(my_menu_win, 1, 0, 40, "My Menu", COLOR_PAIR(1));
        mvwaddch(my_menu_win, 2, 0, ACS_LTEE);
        mvwhline(my_menu_win, 2, 1, ACS_HLINE, 38);
        mvwaddch(my_menu_win, 2, 39, ACS_RTEE);
        
        /* Post the menu */
        post_menu(my_menu);
        wrefresh(my_menu_win);
 
        while((c = wgetch(my_menu_win)) != KEY_F(1))
        {       switch(c)
                {      case KEY_DOWN:
                               menu_driver(my_menu, REQ_DOWN_ITEM);
                               break;
                       case KEY_UP:
                               menu_driver(my_menu, REQ_UP_ITEM);
                               break;
               }
                wrefresh(my_menu_win);
        }       
 
        /* Unpost and free all the memory taken up */
        unpost_menu(my_menu);
        free_menu(my_menu);
        for(i = 0; i < n_choices; ++i)
                free_item(my_items[i]);
        endwin();
}
 
void print_in_middle(WINDOW *win, int starty, int startx, int width, char *string, chtype color)
{       int length, x, y;
        float temp;
 
        if(win == NULL)
               win = stdscr;
        getyx(win, y, x);
        if(startx != 0)
               x = startx;
        if(starty != 0)
               y = starty;
        if(width == 0)
               width = 80;
 
        length = strlen(string);
        temp = (width - length)/ 2;
        x = startx + (int)temp;
        wattron(win, color);
        mvwprintw(win, y, x, "%s", string);
        wattroff(win, color);
        refresh();
}

Acest exemplu creează un meniu cu titlu şi margine şi o linie ce desparte titlul de opţiuni. După cum vedeţi, pentru a ataşa o fereastră unui meniu trebuie să folosiţi funcţia set_menu_win(). Apoi ataşăm şi subfereastra. Aceasta afişează opţiunile. Puteţi să setaţi şirul de caractere ce apare în partea stângă a unei opţiuni selectate cu set_menu_mark().

Meniuri Scrolabile

Dacă subfereastra dată pentru o fereastră nu e destul de  mare pentru a afişa toate opţiunile atunci meniul va fi scrolabil. Atunci când sunteţi pe opţiunea cea mai de jos din lista afişată, dacă trimiteţi REQ_DOWN_ITEM, va fi translatată REQ_SCR_DLINE şi meniul va fi scrolat cu o opţiune. Puteţi da manual operaţii REQ_SCR_ pentru a face scroll. Haideţi să vedem cum se poate face.

Exemplu cu Meniuri Scrolabile

/* File Path: menus/menu_scroll.c */
#include <menu.h>
 
#define ARRAY_SIZE(a) (sizeof(a) / sizeof(a[0]))
#define CTRLD  4
 
char *choices[] = {
                        "Choice 1",
                        "Choice 2",
                        "Choice 3",
                        "Choice 4",
                       "Choice 5",
                       "Choice 6",
                       "Choice 7",
                       "Choice 8",
                       "Choice 9",
                       "Choice 10",
                        "Exit",
                        (char *)NULL,
                  };
void print_in_middle(WINDOW *win, int starty, int startx, int width, char *string, chtype color);
 
int main()
{       ITEM **my_items;
        int c;                         
        MENU *my_menu;
        WINDOW *my_menu_win;
        int n_choices, i;
        
        /* Initialize curses */
        initscr();
        start_color();
        cbreak();
        noecho();
        keypad(stdscr, TRUE);
        init_pair(1, COLOR_RED, COLOR_BLACK);
        init_pair(2, COLOR_CYAN, COLOR_BLACK);
 
        /* Create items */
        n_choices = ARRAY_SIZE(choices);
        my_items = (ITEM **)calloc(n_choices, sizeof(ITEM *));
        for(i = 0; i < n_choices; ++i)
                my_items[i] = new_item(choices[i], choices[i]);
 
        /* Crate menu */
        my_menu = new_menu((ITEM **)my_items);
 
        /* Create the window to be associated with the menu */
        my_menu_win = newwin(10, 40, 4, 4);
        keypad(my_menu_win, TRUE);
     
        /* Set main window and sub window */
        set_menu_win(my_menu, my_menu_win);
        set_menu_sub(my_menu, derwin(my_menu_win, 6, 38, 3, 1));
        set_menu_format(my_menu, 5, 1);
                       
        /* Set menu mark to the string " * " */
        set_menu_mark(my_menu, " * ");
 
        /* Print a border around the main window and print a title */
        box(my_menu_win, 0, 0);
        print_in_middle(my_menu_win, 1, 0, 40, "My Menu", COLOR_PAIR(1));
        mvwaddch(my_menu_win, 2, 0, ACS_LTEE);
        mvwhline(my_menu_win, 2, 1, ACS_HLINE, 38);
        mvwaddch(my_menu_win, 2, 39, ACS_RTEE);
        
        /* Post the menu */
        post_menu(my_menu);
        wrefresh(my_menu_win);
        
        attron(COLOR_PAIR(2));
        mvprintw(LINES - 2, 0, "Use PageUp and PageDown to scoll up or down a page of items");
        mvprintw(LINES - 1, 0, "Arrow Keys to navigate (F1 to Exit)");
        attroff(COLOR_PAIR(2));
        refresh();
 
        while((c = wgetch(my_menu_win)) != KEY_F(1))
        {       switch(c)
                {      case KEY_DOWN:
                               menu_driver(my_menu, REQ_DOWN_ITEM);
                               break;
                       case KEY_UP:
                               menu_driver(my_menu, REQ_UP_ITEM);
                               break;
                       case KEY_NPAGE: /* Page Down */
                               menu_driver(my_menu, REQ_SCR_DPAGE);
                               break;
                       case KEY_PPAGE: /* Page Up  */
                               menu_driver(my_menu, REQ_SCR_UPAGE);
                               break;
               }
                wrefresh(my_menu_win);
        }       
 
        /* Unpost and free all the memory taken up */
        unpost_menu(my_menu);
        free_menu(my_menu);
        for(i = 0; i < n_choices; ++i)
                free_item(my_items[i]);
        endwin();
}
 
void print_in_middle(WINDOW *win, int starty, int startx, int width, char *string, chtype color)
{       int length, x, y;
        float temp;
 
        if(win == NULL)
               win = stdscr;
        getyx(win, y, x);
        if(startx != 0)
               x = startx;
        if(starty != 0)
               y = starty;
        if(width == 0)
               width = 80;
 
        length = strlen(string);
        temp = (width - length)/ 2;
        x = startx + (int)temp;
        wattron(win, color);
        mvwprintw(win, y, x, "%s", string);
        wattroff(win, color);
        refresh();
}

Acest program se înţelege de la sine. În acest exemplu numărul de opţiuni a fost mărit la 10, care e mai mare decât dimensiunea subferestrei care poate conţine maxim 6 opţiuni. Acest mesaj trebuie transmis sistemului de meniuri în mod explicit folosind funcţia set_menu_format(). Aici specificăm numărul de rânduri şi de coloane care vrem să fie afişate într-o pagină. Putem să specificăm orice număr de opţiuni pentru afişare, în varaibilă pentru rânduri, dacă e mai mic decât înălţimea subferestrei. Dacă tasta apăsată de utilizator e PAGE UP sau PAGE DOWN, meniul e scrolat cu o pagină conform cererilor (REQ_SCR_DPAGE şi REQ_SCR_UPAGE) transmise lui menu_driver().

Meniuri Multi Coloană

În exemplu de mai sus am văzut cum se foloseşte funcţia set_menu_format(). Nu am specificat ce face parametrul al treilea(pentru coloane). Ei bine, dacă subfereastra este destul de lată, puteţi opta să afişaţi mai multe opţiuni pe un rând. Aceast lucru poate fi specificat în parametru pentru coloane. Pentru a face lucrurile mai simple, următorul exemplu nu afişează descrieri pentru opţiuni.

Examplu cu Meniuri Multi Coloană

/* File Path: menus/menu_multi_column.c */
#include <menu.h>
 
#define ARRAY_SIZE(a) (sizeof(a) / sizeof(a[0]))
#define CTRLD  4
 
char *choices[] = {
                        "Choice 1", "Choice 2", "Choice 3", "Choice 4", "Choice 5",
                       "Choice 6", "Choice 7", "Choice 8", "Choice 9", "Choice 10",
                       "Choice 11", "Choice 12", "Choice 13", "Choice 14", "Choice 15",
                       "Choice 16", "Choice 17", "Choice 18", "Choice 19", "Choice 20",
                        "Exit",
                        (char *)NULL,
                  };
 
int main()
{       ITEM **my_items;
        int c;                         
        MENU *my_menu;
        WINDOW *my_menu_win;
        int n_choices, i;
        
        /* Initialize curses */
        initscr();
        start_color();
        cbreak();
        noecho();
        keypad(stdscr, TRUE);
        init_pair(1, COLOR_RED, COLOR_BLACK);
        init_pair(2, COLOR_CYAN, COLOR_BLACK);
 
        /* Create items */
        n_choices = ARRAY_SIZE(choices);
        my_items = (ITEM **)calloc(n_choices, sizeof(ITEM *));
        for(i = 0; i < n_choices; ++i)
                my_items[i] = new_item(choices[i], choices[i]);
 
        /* Crate menu */
        my_menu = new_menu((ITEM **)my_items);
 
        /* Set menu option not to show the description */
        menu_opts_off(my_menu, O_SHOWDESC);
 
        /* Create the window to be associated with the menu */
        my_menu_win = newwin(10, 70, 4, 4);
        keypad(my_menu_win, TRUE);
     
        /* Set main window and sub window */
        set_menu_win(my_menu, my_menu_win);
        set_menu_sub(my_menu, derwin(my_menu_win, 6, 68, 3, 1));
        set_menu_format(my_menu, 5, 3);
        set_menu_mark(my_menu, " * ");
 
        /* Print a border around the main window and print a title */
        box(my_menu_win, 0, 0);
        
        attron(COLOR_PAIR(2));
        mvprintw(LINES - 3, 0, "Use PageUp and PageDown to scroll");
        mvprintw(LINES - 2, 0, "Use Arrow Keys to navigate (F1 to Exit)");
        attroff(COLOR_PAIR(2));
        refresh();
 
        /* Post the menu */
        post_menu(my_menu);
        wrefresh(my_menu_win);
        
        while((c = wgetch(my_menu_win)) != KEY_F(1))
        {       switch(c)
                {      case KEY_DOWN:
                               menu_driver(my_menu, REQ_DOWN_ITEM);
                               break;
                       case KEY_UP:
                               menu_driver(my_menu, REQ_UP_ITEM);
                               break;
                       case KEY_LEFT:
                               menu_driver(my_menu, REQ_LEFT_ITEM);
                               break;
                       case KEY_RIGHT:
                               menu_driver(my_menu, REQ_RIGHT_ITEM);
                               break;
                       case KEY_NPAGE:
                               menu_driver(my_menu, REQ_SCR_DPAGE);
                               break;
                       case KEY_PPAGE:
                               menu_driver(my_menu, REQ_SCR_UPAGE);
                               break;
               }
                wrefresh(my_menu_win);
        }       
 
        /* Unpost and free all the memory taken up */
        unpost_menu(my_menu);
        free_menu(my_menu);
        for(i = 0; i < n_choices; ++i)
                free_item(my_items[i]);
        endwin();
}

Priviţi apelul funcţiei set_menu_format(). Specifică numărul de coloane la 3, aşa încât se afişează 3 opţiuni pe rând. De asemeni am dezactivat descrierile pentru opţiuni cu funcţia menu_opts_off(). Sunt câteva funcţii set_menu_opts(), menu_opts_on() şi menu_opts() care se pot folosi pentru a modifica proprietăţile meniului. Următoarele proprietăţi pot fi specificate.

       O_ONEVALUE
            Numai o opţiune poate fi selectată pentru acest meniu.
 
       O_SHOWDESC
            Afişează descrierea opţiunii când meniul este postat.
 
       O_ROWMAJOR
            Afişează meniul în ordinea crescătoare a rândurilor.
 
       O_IGNORECASE
Ignoră tipul caracterelor când se face potrivire după patern.
 
       O_SHOWMATCH
Mută cursorul în interiorul numelui opţiunii când se face potrivire după patern. 
       O_NONCYCLIC
            Nu se poate trece de la ultima opţiune la prima şi invers.

Toate proprietăţile sunt iniţial active. Puteţi active sau active o anumită proprietate cu funcţiile menu_opts_on() şi menu_opts_off(). Puteţi deasemeni folosi set_menu_opts() pentru a specifica direct proprietăţile. Argumentul aceste funcţii ar trebui să fie o valoare foramtă cu OR din constantele de mai sus. Funcţia menu_opts() poate fi folosită pentru a afla proprietăţile curente.

Meniuri Multi Valori

Poate vă întrebaţi ce se întâmplă dacă deactivaţi O_ONEVALUE. Meniul devine unul cu multi-valori. Aceasta înseamnă că puteţi selecta mai multe opţiuni. Aceasta ne adduce la cererea REQ_TOGGLE_ITEM. Haideţi să vedem.

Examplu cu Meniuri Multi Valori

/* File Path: menus/menu_toggle.c */
#include <menu.h>
 
#define ARRAY_SIZE(a) (sizeof(a) / sizeof(a[0]))
#define CTRLD  4
 
char *choices[] = {
                        "Choice 1",
                        "Choice 2",
                        "Choice 3",
                        "Choice 4",
                       "Choice 5",
                       "Choice 6",
                       "Choice 7",
                        "Exit",
                  };
 
int main()
{       ITEM **my_items;
        int c;                         
        MENU *my_menu;
        int n_choices, i;
        ITEM *cur_item;
        
        /* Initialize curses */        
        initscr();
        cbreak();
        noecho();
        keypad(stdscr, TRUE);
 
        /* Initialize items */
        n_choices = ARRAY_SIZE(choices);
        my_items = (ITEM **)calloc(n_choices + 1, sizeof(ITEM *));
        for(i = 0; i < n_choices; ++i)
                my_items[i] = new_item(choices[i], choices[i]);
        my_items[n_choices] = (ITEM *)NULL;
 
        my_menu = new_menu((ITEM **)my_items);
 
        /* Make the menu multi valued */
        menu_opts_off(my_menu, O_ONEVALUE);
 
        mvprintw(LINES - 3, 0, "Use <SPACE> to select or unselect an item.");
        mvprintw(LINES - 2, 0, "<ENTER> to see presently selected items(F1 to Exit)");
        post_menu(my_menu);
        refresh();
 
        while((c = getch()) != KEY_F(1))
        {       switch(c)
                {      case KEY_DOWN:
                               menu_driver(my_menu, REQ_DOWN_ITEM);
                               break;
                       case KEY_UP:
                               menu_driver(my_menu, REQ_UP_ITEM);
                               break;
                       case ' ':
                               menu_driver(my_menu, REQ_TOGGLE_ITEM);
                               break;
                       case 10:       /* Enter */
                       {       char temp[200];
                               ITEM **items;
 
                               items = menu_items(my_menu);
                               temp[0] = '\0';
                               for(i = 0; i < item_count(my_menu); ++i)
                                      if(item_value(items[i]) == TRUE)
                                      {       strcat(temp, item_name(items[i]));
                                              strcat(temp, " ");
                                      }
                               move(20, 0);
                               clrtoeol();
                               mvprintw(20, 0, temp);
                               refresh();
                       }
                       break;
               }
        }       
        unpost_menu(my_menu);
        for(i = 0; i < n_choices; ++i)
               free_item(my_items[i]);
        free_menu(my_menu);
        endwin();
}

Wow, o mulţime de funcţii noi. Să o luăm pe fiecare în parte. În primul rând REQ_TOGGLE_ITEM. Într-un meniu multi -valori, utilizatorul trebuie lăsat să selecteze una sau mai multe opţiuni. Cererea REQ_TOGGLE_ITEM un/setează selecţia curentă. În acest caz căn se apasă spaţiu se trimite cererea REQ_TOGGLE_ITEM lui menu_driver pentru a afla rezultatul.

Acum când utilizatorul apasă <ENTER> afişăm opţiunile selectate de utilizator. Întâi afl ăm care sunt opţiunile din meniu cu funcţia menu_items(). Apoi parcurgem toate opţiunile să vedem care e selectată. Funcţia item_value() returnează TRUE dacă o opţiune e selectată. Funcţia item_count() returnează numărul opţiunilor din meniu. Numele opţiunii poate fi aflat cu funcţia item_name(). Puteţi afla şi descrierea asociată unei opţiuni folosind item_description().

Atributele Meniului

Ei bine, de acum cred că doriţi să aveţi un meniu puţin mai special cu mai multă funcţionalitate. Ştiu. Vreţi Culori !!!. Vreţi să creaţi meniuri similare cu cele din jocurile dos. Funcţiile set_menu_fore() şi set_menu_back() pot fi folosite pentru a schimba atributele opţiunilor selectate şi neselectate. Acest lucru nu prea reiese din nume. Nu schimbă culorile fundalului şi a fondului cum s-ar crede.

Funcţia set_menu_grey() poate fi folosită pentru a schimba atributul pentru opţiunile neselectabile. Am ajuns la proprietatea O_SELECTABLE. Putem să o dezactivăm cu item_opts_off() şi după aceea acea opţiune nu mai este selectabilă. Este ca o opţiune dezactivată din windows. Să vedem aceste concepte într-un exemplu.

Exemplu cu attribute ale meniului

/* File Path: menus/menu_attrib.c */
#include <menu.h>
 
#define ARRAY_SIZE(a) (sizeof(a) / sizeof(a[0]))
#define CTRLD  4
 
char *choices[] = {
                        "Choice 1",
                        "Choice 2",
                        "Choice 3",
                        "Choice 4",
                       "Choice 5",
                       "Choice 6",
                       "Choice 7",
                        "Exit",
                  };
 
int main()
{       ITEM **my_items;
        int c;                         
        MENU *my_menu;
        int n_choices, i;
        ITEM *cur_item;
        
        /* Initialize curses */        
        initscr();
        start_color();
        cbreak();
        noecho();
        keypad(stdscr, TRUE);
        init_pair(1, COLOR_RED, COLOR_BLACK);
        init_pair(2, COLOR_GREEN, COLOR_BLACK);
        init_pair(3, COLOR_MAGENTA, COLOR_BLACK);
 
        /* Initialize items */
        n_choices = ARRAY_SIZE(choices);
        my_items = (ITEM **)calloc(n_choices + 1, sizeof(ITEM *));
        for(i = 0; i < n_choices; ++i)
                my_items[i] = new_item(choices[i], choices[i]);
        my_items[n_choices] = (ITEM *)NULL;
        item_opts_off(my_items[3], O_SELECTABLE);
        item_opts_off(my_items[6], O_SELECTABLE);
 
        /* Create menu */
        my_menu = new_menu((ITEM **)my_items);
 
        /* Set fore ground and back ground of the menu */
        set_menu_fore(my_menu, COLOR_PAIR(1) | A_REVERSE);
        set_menu_back(my_menu, COLOR_PAIR(2));
        set_menu_grey(my_menu, COLOR_PAIR(3));
 
        /* Post the menu */
        mvprintw(LINES - 3, 0, "Press <ENTER> to see the option selected");
        mvprintw(LINES - 2, 0, "Up and Down arrow keys to naviage (F1 to Exit)");
        post_menu(my_menu);
        refresh();
 
        while((c = getch()) != KEY_F(1))
        {       switch(c)
                {      case KEY_DOWN:
                               menu_driver(my_menu, REQ_DOWN_ITEM);
                               break;
                       case KEY_UP:
                               menu_driver(my_menu, REQ_UP_ITEM);
                               break;
                       case 10: /* Enter */
                               move(20, 0);
                               clrtoeol();
                               mvprintw(20, 0, "Item selected is : %s", 
                                              item_name(current_item(my_menu)));
                               pos_menu_cursor(my_menu);
                               break;
               }
        }       
        unpost_menu(my_menu);
        for(i = 0; i < n_choices; ++i)
               free_item(my_items[i]);
        free_menu(my_menu);
        endwin();
}

 

Utilitatea Pointerilor utilizator

Putem asocial un pointer utilizator pentru fiecare opţiune din meniu. E la fel ca pointerul utilizator de la paneluri. Nu este modificat de sistemul de meniuri. Puteţi stoca orice doriţi în acel pointer. De obicei îl folosesc pentru a stoca o funcţie care e apelată când se selectează o opţiune din meniu (E selectată şi utilizatorul a apăsat <ENTER>);

Exemplu Pointeri Utilizator

/* File Path: menus/menu_userptr.c */
#include <menu.h>
 
#define ARRAY_SIZE(a) (sizeof(a) / sizeof(a[0]))
#define CTRLD  4
 
char *choices[] = {
                        "Choice 1",
                        "Choice 2",
                        "Choice 3",
                        "Choice 4",
                       "Choice 5",
                       "Choice 6",
                       "Choice 7",
                        "Exit",
                  };
void func(char *name);
 
int main()
{       ITEM **my_items;
        int c;                         
        MENU *my_menu;
        int n_choices, i;
        ITEM *cur_item;
        
        /* Initialize curses */        
        initscr();
        start_color();
        cbreak();
        noecho();
        keypad(stdscr, TRUE);
        init_pair(1, COLOR_RED, COLOR_BLACK);
        init_pair(2, COLOR_GREEN, COLOR_BLACK);
        init_pair(3, COLOR_MAGENTA, COLOR_BLACK);
 
        /* Initialize items */
        n_choices = ARRAY_SIZE(choices);
        my_items = (ITEM **)calloc(n_choices + 1, sizeof(ITEM *));
        for(i = 0; i < n_choices; ++i)
        {       my_items[i] = new_item(choices[i], choices[i]);
               /* Set the user pointer */
               set_item_userptr(my_items[i], func);
        }
        my_items[n_choices] = (ITEM *)NULL;
 
        /* Create menu */
        my_menu = new_menu((ITEM **)my_items);
 
        /* Post the menu */
        mvprintw(LINES - 3, 0, "Press <ENTER> to see the option selected");
        mvprintw(LINES - 2, 0, "Up and Down arrow keys to naviage (F1 to Exit)");
        post_menu(my_menu);
        refresh();
 
        while((c = getch()) != KEY_F(1))
        {       switch(c)
                {      case KEY_DOWN:
                               menu_driver(my_menu, REQ_DOWN_ITEM);
                               break;
                       case KEY_UP:
                               menu_driver(my_menu, REQ_UP_ITEM);
                               break;
                       case 10: /* Enter */
                       {       ITEM *cur;
                               void (*p)(char *);
 
                               cur = current_item(my_menu);
                               p = item_userptr(cur);
                               p((char *)item_name(cur));
                               pos_menu_cursor(my_menu);
                               break;
                       }
                       break;
               }
        }       
        unpost_menu(my_menu);
        for(i = 0; i < n_choices; ++i)
               free_item(my_items[i]);
        free_menu(my_menu);
        endwin();
}
 
void func(char *name)
{       move(20, 0);
        clrtoeol();
        mvprintw(20, 0, "Item selected is : %s", name);
}

Librăria Form

Ei bine. Dacă aţi văzut acele formulare în paginile web pe care trebuie să le completaţi pentru a face diverse lucruri poate vă întrebaţi cum se pot crea aceste formulare în mod text. E cam dificil de creat în modul ncurses. Librăria form încearcă să ofere baza pentru crearea şi manipularea uşoară formularelor. Are o mulţime de funcţii care se ocupă de validare etc.. Să ne uităm mai în detaliu.

Un formular este o colecţie de câmpuri; fiecare camp poate fi un label(text static) sau locaţie pentru date. Această librărie oferă şi funcţii de a împărţi formularele în pagini.

Fundamente

Formularele sunt create cam în acelaşi mod ca şi meniurile. Întâi se crează câmpurile cu funcţia new_field(). Puteţi seta proprietăţile pentru câmpuri, aşa încât ele sunt afişate cu diverse atribute, validate înainte să piardă focusul etc.. Apoi câmpurile sunt ataşate formularului. După aceasta, formularul poate fi afişat şi să aştepte input de la utilizator. La fel ca şi menu_driver(), formularul e manipulat cu form_driver(). Putem trimite cereri formularului cu form_driver pentru a muta focusul pe un anumit câmp, să mutăm cursorul pe un anumit câmp etc.. După ce userul introduce valori în câmpuri şi se face validarea, formularul poate fi distrus şi memoria dealocată.

Paşii care trebuie urmaţi sunt următorii.

  1. Initializarea curses
  2. Crearea câmpurilor cu new_field(). Puteţi da înălţimea şi lăţimea câmpului, and şi poziţia câmpului în formular.
  3. Crearea formularului cu new_form() specificând câmpurile pe care le va conţine.
  4. Plasarea formularului cu form_post() şi reafişarea ecranului.
  5. Procesarea intrărilor utilizatorului cu o buclă şi realizarea updatatârilor şi a anumitor modificări cu form_driver.
  6. Distrugerea formularului cu form_unpost()
  7. Eliberarea memoriei alocate formularului cu free_form()
  8. Eliberarea memoriei alocate câmpurilor cu free_field()
  9. Terminarea lui curses

După cum vedeţi, lucrul cu librăria form este apoximativ la fel cu lucrul cu librăria menu. Următorul exemplu va arăta câteva exemple despre procesarea formularelor. Să începem călătoria cu un simplu exemplu. Pentru început

Compilarea cu librăria Form

Pentru a folosi funcţiile di librăria form trebuie să includeţi form.h şi să faceţi legătura cu librăria form folosind flagul -lform înainte de flagul -lncurses.

        #include <form.h>
        .
        .
        .
 
        compile and link: gcc <program file> -lform -lncurses

Un Simplu Exemplu

/* File Path: forms/form_simple.c */
#include <form.h>
 
int main()
{       FIELD *field[3];
        FORM  *my_form;
        int ch;
        
        /* Initialize curses */
        initscr();
        cbreak();
        noecho();
        keypad(stdscr, TRUE);
 
        /* Initialize the fields */
        field[0] = new_field(1, 10, 4, 18, 0, 0);
        field[1] = new_field(1, 10, 6, 18, 0, 0);
        field[2] = NULL;
 
        /* Set field options */
        set_field_back(field[0], A_UNDERLINE);        /* Print a line for the option         */
        field_opts_off(field[0], O_AUTOSKIP);         /* Don't go to next field when this */
                                              /* Field is filled up          */
        set_field_back(field[1], A_UNDERLINE); 
        field_opts_off(field[1], O_AUTOSKIP);
 
        /* Create the form and post it */
        my_form = new_form(field);
        post_form(my_form);
        refresh();
        
        mvprintw(4, 10, "Value 1:");
        mvprintw(6, 10, "Value 2:");
        refresh();
 
        /* Loop through to get user requests */
        while((ch = getch()) != KEY_F(1))
        {       switch(ch)
               {       case KEY_DOWN:
                               /* Go to next field */
                               form_driver(my_form, REQ_NEXT_FIELD);
                               /* Go to the end of the present buffer */
                               /* Leaves nicely at the last character */
                               form_driver(my_form, REQ_END_LINE);
                               break;
                       case KEY_UP:
                               /* Go to previous field */
                               form_driver(my_form, REQ_PREV_FIELD);
                               form_driver(my_form, REQ_END_LINE);
                               break;
                       default:
                               /* If this is a normal character, it gets */
                               /* Printed                              */    
                               form_driver(my_form, ch);
                               break;
               }
        }
 
        /* Un post form and free the memory */
        unpost_form(my_form);
        free_form(my_form);
        free_field(field[0]);
        free_field(field[1]); 
 
        endwin();
        return 0;
}

Exemplul de mai sus e destul de simplu. Creează două câmpuri cu funcţia new_field(). new_field() ia ca parametri înălţimea, lăţimea, starty, startx, numărul de rânduri offscreen rows şi numărul adiţional de bufere de lucru. Al cincelea parametru numărul de rânduri offscreen specifică cât dintr-un câmp să fie afişat. Dacă e zero, se afişează întregul câmp altfel formularul va fi scrolabil când utilizatorul accesează părţi neafişate a câmpului. Librăria form alocă un buffer  pentru fiecare camp pentru a stoca datele introduce de utilizator. Utilizarea ultimului parametru din new_field() e pentru a specifica alte buffere. Acestea pot fi folosite pentru orice doriţi.

După ce se creează câmpurile, se setează atributul de back ground la o linie  cu set_field_back(). Opţiunea AUTOSKIP e dezactivată utilizând field_opts_off(). Dacă această opţiune e activată, focusul se va muta pe câmpul următor când câmpul current e umplut complet.

După ce se ataşează câmpurile la formular acesta este plasat pe ecran. După asta, intrările utilizatorului sunt procesate într-o buclă, realizând cererile necesare la form_driver. Detalii despre cererile ce pot fi trimise spre form_driver() vor fi explicate mai târziu.

Jocul cu câmpurile

Fiecare câmp are o mulţime de atribute. Acestea pot fi modificate pentru a obţine ce doriţi şi pentru distracţie !!!. Dec ice mai aşteptăm?

Obţinerea mărimii şi poziţiei unui câmp

Parametrii pe care i-am dat la crearea unui camp pot fi obţinuţi cu field_info(). Returnează înălţimea, lăţimea, starty, startx, numărul de rânduri offscreen rows şi numărul adiţional de bufere de lucru în parametrul dat ca argument. E ca un invers al new_field().

int field_info( FIELD *field,              /* câmpul ce ne interesează */
                int *height, *int width,   /* mărimea câmpului */ 
                int *top, int *left,       /* colţul stânga sus */
               int *offscreen,            /* numărul de rânduri off-screen */
               int *nbuf);                /* dumărul de buffere de lucru */

Mutarea câmpului

Locaţia unui câmp poate fi mutată cu move_field().

int move_field( FIELD *field,              /* câmpul de modificat */
                int top, int left);        /* noul colţ stânga sus */

Deasemeni putem afla noua poziţie cu field_infor().

Justificare unui câmp

Justificare ce trebuie făcută asupra unui camp poate fi setată cu set_field_just().

        int set_field_just(FIELD *field,       /* câmpul de modificat */
                          int justmode);         /* modul de setat */
        int field_just(FIELD *field);           /* returnează modul de justificare al câmpului */

Modurile de justicare acceptate şi returnate de aceste funcţii sunt NO_JUSTIFICATION, JUSTIFY_RIGHT, JUSTIFY_LEFT, or JUSTIFY_CENTER.

Atributul de afişare al unui câmp

După cum aţi văzut în exemplul de mai sus atributul de afişare al unui camp poate fi setat cu set_field_fore() şi setfield_back(). Aceste funcţii setează atributele de foreground şi background a câmpurilor. Puteţi deasemeni să specificaţi un character care va fi afişat în porţiune necompletată a unui câmp. Acest character e setat cu funcţia set_field_pad(). Caracterul iniţial este spaţiu. Funcţiile field_fore(), field_back, field_pad() pot fi folosite prntru a afla atributele de foreground, background şi caracterul special a câmpului. În următoarea listă se află descrierea acestor funcţii.

int set_field_fore(FIELD *field,        /* câmpul de modificat */
                   chtype attr);        /* atributul de setat */ 
 
chtype field_fore(FIELD *field);        /* un câmp */
                                      /* returnează atributl de foreground*/
 
int set_field_back(FIELD *field,        /* câmpul de modificat */
                  chtype attr);        /* attribute to set */ 
 
chtype field_back(FIELD *field);        /* un câmp */
                                      /* returnează atributl de background */
 
int set_field_pad(FIELD *field,         /* câmpul de modificat */
                 int pad);             /* caracterul de setat */ 
 
chtype field_pad(FIELD *field);               /* un câmp */  
                                      /* returnează caracterul setat */
 

Deşi funcţiile de mai sus par simple folosirea culorilor cu set_field_fore() poate fi cam frustantă la îneput. Să vă explic câte ceva despre atributele foreground şi background a unui câmp. Atributul foreground e asociat cu un caracter. Aceasta înseamnă că un character ce îl afişaţi are setat atributul respectiv (setat cu set_field_fore()). Atributul background e atributul folosit pentru a umple fundalul câmpului, chiar dacă există sau nu vreun caracter. Culori? Deoarece culorile sunt folosite în perechi care e modul corect de a le folosi? Iată un exemplu care va lumina această problemă.

Exemplu cu Atributele de afişare

/* File Path: forms/form_attrib.c */
#include <form.h>
 
int main()
{       FIELD *field[3];
        FORM  *my_form;
        int ch;
        
        /* Initialize curses */
        initscr();
        start_color();
        cbreak();
        noecho();
        keypad(stdscr, TRUE);
 
        /* Initialize few color pairs */
        init_pair(1, COLOR_WHITE, COLOR_BLUE);
        init_pair(2, COLOR_WHITE, COLOR_BLUE);
 
        /* Initialize the fields */
        field[0] = new_field(1, 10, 4, 18, 0, 0);
        field[1] = new_field(1, 10, 6, 18, 0, 0);
        field[2] = NULL;
 
        /* Set field options */
        set_field_fore(field[0], COLOR_PAIR(1));/* Put the field with blue background */
        set_field_back(field[0], COLOR_PAIR(2));/* and white foreground (characters */
                                              /* are printed in white        */
        field_opts_off(field[0], O_AUTOSKIP);         /* Don't go to next field when this */
                                              /* Field is filled up          */
        set_field_back(field[1], A_UNDERLINE); 
        field_opts_off(field[1], O_AUTOSKIP);
 
        /* Create the form and post it */
        my_form = new_form(field);
        post_form(my_form);
        refresh();
        
        set_current_field(my_form, field[0]); /* Set focus to the colored field */
        mvprintw(4, 10, "Value 1:");
        mvprintw(6, 10, "Value 2:");
        mvprintw(LINES - 2, 0, "Use UP, DOWN arrow keys to switch between fields");
        refresh();
 
        /* Loop through to get user requests */
        while((ch = getch()) != KEY_F(1))
        {       switch(ch)
               {       case KEY_DOWN:
                               /* Go to next field */
                               form_driver(my_form, REQ_NEXT_FIELD);
                               /* Go to the end of the present buffer */
                               /* Leaves nicely at the last character */
                               form_driver(my_form, REQ_END_LINE);
                               break;
                       case KEY_UP:
                               /* Go to previous field */
                               form_driver(my_form, REQ_PREV_FIELD);
                               form_driver(my_form, REQ_END_LINE);
                               break;
                       default:
                               /* If this is a normal character, it gets */
                               /* Printed                              */    
                               form_driver(my_form, ch);
                               break;
               }
        }
 
        /* Un post form and free the memory */
        unpost_form(my_form);
        free_form(my_form);
        free_field(field[0]);
        free_field(field[1]); 
 
        endwin();
        return 0;
}

Jucaţivă cu perechile de culori şi încercaţi să înţelegeţi atributele de foreground şi background. În programele mele de obicei folosesc numai atributul background setându-l cu set_field_back(). Curses nu poate folosi attribute individuale de culori.

 

Opţiunea câmp de biţi

Există deasemeni o mulţime de opţiuni de camp pe biţi ce pot fi setate pentru a modifica un formular. Puteţi să le modificaţi cu aceste funcţii:

int set_field_opts(FIELD *field,          /* câmpul de modificat */
                   int attr);             /* atributele de setat */ 
 
int field_opts_on(FIELD *field,           /* câmpul de modificat */
                 int attr);              /* atributele de activat */ 
 
int field_opts_off(FIELD *field,          /* câmpul de modificat */
                  int attr);             /* atributele de dezactivat */ 
 
int field_opts(FIELD *field);             /* un câmp */ 

Funcţia set_field_opts() poate fi folosită pentru a seta direct atributele unui camp sau puteţi folos field_opts_on() şi field_opts_off() pentru a le active şi dezactiva. Puteţi afla oricând atributele unui camp cu field_opts(). Urmează o listă a opţiunilor prezente pentru un câmp. Iniţial, toate opţiunile sunt activate.

O_VISIBLE

Controlează dacă câmpul e afişat pe ecran. Poate fi folosit în timpul procesării formularului pentru a ascunde sau afişa câmpurile în funcţie de valoarea câmpului părinte.

O_ACTIVE

Controlează dacă câmpul e activ la procesarea formularului (i.e. vizitat de tastele funcţionale ale formularului). Poate fi folosit pentru crearea labelurilor sau câmpurilor derivate cu valorile bufferelor schimbate de aplicaţia formular.

O_PUBLIC

Controlează dacă datele sunt afişate la intrare. Dacă această opţiune e dezactivată pentru un camp librăria va accepta date şi le va edita în acel câmp dar nu vor fi vizibile şi nici cursorul nu va fi afişat. Puetţi dezactiva bitul O_PUBLIC pentru a defini câmpuri parolă.

O_EDIT

Controlează dacă datele dintr-un camp pot fi modificate. Dacă această opţiune e dezactivată toate cererile de editare înafară de REQ_PREV_CHOICE şi REQ_NEXT_CHOICE nu vor funcţiona. Astfel de câmpuri pot fi folosite pentru mesaje de ajutor.

O_WRAP

Controlează word-wrapping într-un câmp multilinie. În mod normal, când un caracter al uni cuvânt ajunge la sfârşit de linie tot cuvâtul e mutat pe linia următoare dacă există. Dacă această opţiune e dezactivată cuvăntul va fi împărţit de la acel caracter.

O_BLANK

Controlează golirea câmpului. Când această opţiune e activată introducerea unui character la începutul acelui câmp face ca conţinutul lui să fie şters (cu excepţia caracterului de abia introdus).

O_AUTOSKIP

Controlează modul de trecere la următorul camp când acesta e umplut. În mod normal, când un utilizator încearcă să introducă mai mult text într-un camp decât încape, locaţia de editare trece la următorul câmp. Când această opţiune e dezactivată cursorul se va bloca la sfârşitul câmpului respectiv. Această opţiune e ignorată când se folosesc câmpuri dinamice.

O_NULLOK

Controlează dacă se aplică validarea pentru câmpuri goale. În mod normal nu se validează; un utilizator poate lăsa un câmp gol fără a face validare la ieşire.Dacă această opţiune e dezactivată atunci se face validarea.

O_PASSOK

Controlează dacă se aplică validarea la fiecare ieşire, sau numai după ce câmpul este modificat. În mod normal ultima variantă e corectă. Setând O_PASSOK dacă funcţia de validare diferă pe parcursul procesării formularului.

O_STATIC

Controlează dacă câmpul e fixat pe dimensiunile lui iniţiale. Dacă o dezactivaţi, câmpul devine dimanic şi se măreşte în funcţie de datele introduse.

Opţiunile unui câmp selectat nu pot fi modificate. Totuşi, se pot modifica opţiunile unui câmp ce nu selectat.

Opţiunile sunt măşti de biţi şi pot fi combinate utilizând or. Aţi văzut cum funcţionează opţiunea O_AUTOSKIP. Următorul exemplu clarifică modul de utilizare a unor noi opţiuni. Celelate opţiuni sunt explicate unde va fi necesar.

Exemplu cu Opţiuni ale câmpurilor

/* File Path: forms/form_options.c */
#include <form.h>
 
#define STARTX 15
#define STARTY 4
#define WIDTH 25
 
#define N_FIELDS 3
 
int main()
{       FIELD *field[N_FIELDS];
        FORM  *my_form;
        int ch, i;
        
        /* Initialize curses */
        initscr();
        cbreak();
        noecho();
        keypad(stdscr, TRUE);
 
        /* Initialize the fields */
        for(i = 0; i < N_FIELDS - 1; ++i)
               field[i] = new_field(1, WIDTH, STARTY + i * 2, STARTX, 0, 0);
        field[N_FIELDS - 1] = NULL;
 
        /* Set field options */
        set_field_back(field[1], A_UNDERLINE);        /* Print a line for the option         */
        
        field_opts_off(field[0], O_ACTIVE); /* This field is a static label */
        field_opts_off(field[1], O_PUBLIC); /* This filed is like a password field*/
        field_opts_off(field[1], O_AUTOSKIP); /* To avoid entering the same field */
                                            /* after last character is entered */
        
        /* Create the form and post it */
        my_form = new_form(field);
        post_form(my_form);
        refresh();
        
        set_field_buffer(field[0], 0, "This is a static Field");
        mvprintw(STARTY, STARTX - 10, "Field 1:");
        mvprintw(STARTY + 2, STARTX - 10, "Field 2:");
        refresh();
 
        /* Loop through to get user requests */
        while((ch = getch()) != KEY_F(1))
        {       switch(ch)
               {       case KEY_DOWN:
                               /* Go to next field */
                               form_driver(my_form, REQ_NEXT_FIELD);
                               /* Go to the end of the present buffer */
                               /* Leaves nicely at the last character */
                               form_driver(my_form, REQ_END_LINE);
                               break;
                       case KEY_UP:
                               /* Go to previous field */
                               form_driver(my_form, REQ_PREV_FIELD);
                               form_driver(my_form, REQ_END_LINE);
                               break;
                       default:
                               /* If this is a normal character, it gets */
                               /* Printed                              */    
                               form_driver(my_form, ch);
                               break;
               }
        }
 
        /* Un post form and free the memory */
        unpost_form(my_form);
        free_form(my_form);
        free_field(field[0]);
        free_field(field[1]); 
 
        endwin();
        return 0;
}

Acest exemplu, deşi nefolositor, arată cum pot fi folosite opţiunile. Dacă sunt folosite correct informaţia din formulare poate fi prezentată foarte eficient. Al doile câmp nefiind O_PUBLIC, nu arată caracterele pe care le introduceţi.

Statutul câmpului

Statutul unui câmp specifică dacă un câmp a fost modificat sau nu. Iniţial e setat pe FALSE şi când utilizatorul introduce datele va fi setat pe TRUE. Deci putem afla dacă un câmp a fost modificat sau nu. Următoarele funcţii pot fi folosite.

int set_field_status(FIELD *field,      /* câmpul de modificat */
                   int status);         /* satutul de setat */
 
int field_status(FIELD *field);         /* aflarea statutului unui câmp */

E bine se aflăm statutul unui câmp numai după ce se iese din el, deoarece bufferul de date s-ar putea să nu fie modificat încă. Pentru a garanta că se returnează statutul corect, apelaţi field_status() în (1) rutina de validare la ieşire sau (2) din părţile de terminare a câmpului sau formularului, sau (3) chiar după o cerere de tipul REQ_VALIDATION

Pointer de câmp

Fiecare câmp conţine un pointer ce poate fi utilizat de utilizator pentru ce vor ei. NU este modificat de librăria form şi poate fi utilizat în orice scop. Următoarele funcţii setează şi returnează pointerul.

int set_field_userptr(FIELD *field,   
                  char *userptr);      /* pointerul pe care vreţi să-l ataşaţi de câmpul field   */
 
char *field_userptr(FIELD *field);      /* returnează pointerul ataşat de field */

Câmpuri cu mărime variabilă

Dacă doriţi un câmp să fie cu lungime variabilă atunci trebuie să utilizaţi acestă facilitate la maxim. Acest lucru va oferi utilizatorului un câmp în care se pot introduce mai multe date decât dimensiunea iniţială a câmpului. Conform orientării câmpului se va face scroll orizontal sau vertical.

Pentru a face un câmp să crească dynamic trebuie activată opţiunea O_STATIC. Acest lucru poate fi făcut cu

        field_opts_off(field_pointer, O_STATIC);

Dar nu e indicat să lăsaţi câmpul să crească la infinit. Puteţi să setaţi limita maximă cu

int set_max_field(FIELD *field,    /* Câmpul asupra căruia să faceţi modificări */
                 int max_growth); /* valoarea maximă a dimensiunii câmpului */

Informaţia despre un câmp dinamic poate fi luată cu

int dynamic_field_info( FIELD *field,         /* Câmpul asupra căruia să operaţi */
                       int   *prows,  /* numărul rundurilor */
                       int   *pcols,  /* numărul coloanelor */
                       int   *pmax)   /* limita maximă până la care se poate extinde un câmp */

 

Deşi field_info funcţionează normal, este indicat să luaţi atributele necesare cu această funcţie pentru un câmp dinamic.

Reapelaţi rutina librăriei new_field; un nou câmp cu înălţimea de 1 şi lungimea de un rândsunt definite. Un nou câmp cu înălţime mai mare de unu va fi definit ca un câmp cu mai multi linie.

Un câmp cu o linie cu O_STATIC dezactivat (câmp dinamic) va conţine un rând fix, dar numărul de coloane creşte când utilizatorul introduce mai multe date decât poate conţine câmpul respectiv. Numărul de coloane afişat va fi fix şi datele introduse pot fi scrollate orizontal.

Un câmp multi-linie cu O_STATIC turned dezactivat (câmp dinamic) va conţine un număr fix de coloane, dar numărul de rânduri creşte când utilizatorul introduce mai multe date decât poate conţine câmpul respectiv. Numărul de rânduri afişat va fi fix şi datele introduse pot fi scrollate vertical.

Cele două paragrafe de mai sus explică comportamentul unui câmp dinamic. Modul în care alte părţi ale librăriei form se comportă este explicat mai jos:

1.      Opţiunea O_AUTOSKIP va fi ignorată dacă opţiunea O_STATIC e deactivată şi nu există dimensiune maximă specificată pentru câmp. O_AUTOSKIP generează o cerere REQ_NEXT_FIELD pentru formular când utilizatorul tipăreşte ultimul caracter. Într-un câmp dinamic fără dimensiune maximă specificată, nu există o ultimă poziţie. Dacă o dimensiune maximă este specificată, opţiunea O_AUTOSKIP va funcţiona normal dacă câmpul a ajuns la dimensiunea maximă.

2.      Justificarea câmpului va fi ignorată dacă O_STATIC e deactivată. set_field_poate fi folosit doar pentru JUSTIFY_LEFT, JUSTIFY_RIGHT, JUSTIFY_CENTER pentru conţinutul unei singure linii. Un câmp dimanic pe o linie, prin definiţie, creşte şi poate fi scrolat orizontal şi poate conţine mai multe date decât pentru justificare. field_just nu returnează nimic în plus.

3.      Cererile REQ_NEW_LINE vor opera în acelşi mod indiferent de opţiunea O_NL_OVERLOAD dacă opţiunea O_STATIC e dezactivată şi nu există lungime maximă pe setată. Dacă opţiunea O_NL_OVERLOAD e activată, REQ_NEW_LINE generează implicit REQ_NEXT_FIELD dacă e chemată de pe ultima linie a câmpului. Dacă un câmp poate creşte fără limită, nu există o ultimă linie, aşa încât REQ_NEW_LINE nu va genera niciodată REQ_NEXT_FIELD. Dacă se specifică o limită maximă şi opţiunea O_NL_OVERLOAD e activată, REQ_NEW_LINE va genera implicit REQ_NEXT_FIELD doar dacăcâmpul a crescut la maxim şi utilizatorul e pe ultima linie.

4.      Apelul dup_field va funcţiona normal; va duplica câmpul, incluzând şi mărimea actuală a buferului şi conţinutul câmpului de duplicat.Orice mărime maximă dacă va fi specificată va fi duplicată.

5.      Apelul link_field va funcţiona normal; va duplica toate atributele unui câmp şi va împărţi bufferele cu câmpul de care este legat. Dacă opţiunea O_STATIC is schimbată ulterior de un câmp ce împarte bufferele, cum răspunde sistemul la încercarea de a introduce mai multe date în câmp decât bufferul poate conţine va depinde de setările opţiunii din câmpul curent.

6.      Apelul field_info va funcţiona normal; varaibila nrow va conţine valoarea apelului original la new_field. Utilizatorul va trebui să folosească dynamic_field_info, descrisă mai sus, pentru a afla dimensiunea curentă a bufferului.

Câteva din punctele de mai sus au sens numai după explicaţiile driverului. Ne vom uita peste acestea în continuare.

 

Ferestre Formular

Conceptul ferestrelor formular e similar ca ferestrele menu. Fiecare formular e asociat cu o fereastră principală şi o subfereastră. Fereastra principală titlul sau marginea sau orice altceva doreşte utilizatorul. Apoi subfereastra conţine toate câmpurile şi le afişează conform poziţiei lor. Acest lucru oferă o flexibilitate mare în afişarea unor formulare atrăgătoare.

Deoarece, în mare, e similar cu ferestrele meniu, void a un exemplu fără prea multe explicaţii. Funcţiile sunt similare şi funcţionează cam în acelaşi mod.

Exemplu cu Ferestre Formular

/* File Path: forms/form_win.c */
#include <form.h>
 
void print_in_middle(WINDOW *win, int starty, int startx, int width, char *string, chtype color);
 
int main()
{
        FIELD *field[3];
        FORM  *my_form;
        WINDOW *my_form_win;
        int ch, rows, cols;
        
        /* Initialize curses */
        initscr();
        start_color();
        cbreak();
        noecho();
        keypad(stdscr, TRUE);
 
        /* Initialize few color pairs */
        init_pair(1, COLOR_RED, COLOR_BLACK);
 
        /* Initialize the fields */
        field[0] = new_field(1, 10, 6, 1, 0, 0);
        field[1] = new_field(1, 10, 8, 1, 0, 0);
        field[2] = NULL;
 
        /* Set field options */
        set_field_back(field[0], A_UNDERLINE);
        field_opts_off(field[0], O_AUTOSKIP); /* Don't go to next field when this */
                                            /* Field is filled up           */
        set_field_back(field[1], A_UNDERLINE); 
        field_opts_off(field[1], O_AUTOSKIP);
        
        /* Create the form and post it */
        my_form = new_form(field);
        
        /* Calculate the area required for the form */
        scale_form(my_form, &rows, &cols);
 
        /* Create the window to be associated with the form */
        my_form_win = newwin(rows + 4, cols + 4, 4, 4);
        keypad(my_form_win, TRUE);
 
        /* Set main window and sub window */
        set_form_win(my_form, my_form_win);
        set_form_sub(my_form, derwin(my_form_win, rows, cols, 2, 2));
 
        /* Print a border around the main window and print a title */
        box(my_form_win, 0, 0);
        print_in_middle(my_form_win, 1, 0, cols + 4, "My Form", COLOR_PAIR(1));
        
        post_form(my_form);
        wrefresh(my_form_win);
 
        mvprintw(LINES - 2, 0, "Use UP, DOWN arrow keys to switch between fields");
        refresh();
 
        /* Loop through to get user requests */
        while((ch = wgetch(my_form_win)) != KEY_F(1))
        {       switch(ch)
               {       case KEY_DOWN:
                               /* Go to next field */
                               form_driver(my_form, REQ_NEXT_FIELD);
                               /* Go to the end of the present buffer */
                               /* Leaves nicely at the last character */
                               form_driver(my_form, REQ_END_LINE);
                               break;
                       case KEY_UP:
                               /* Go to previous field */
                               form_driver(my_form, REQ_PREV_FIELD);
                               form_driver(my_form, REQ_END_LINE);
                               break;
                       default:
                               /* If this is a normal character, it gets */
                               /* Printed                              */    
                               form_driver(my_form, ch);
                               break;
               }
        }
 
        /* Un post form and free the memory */
        unpost_form(my_form);
        free_form(my_form);
        free_field(field[0]);
        free_field(field[1]); 
 
        endwin();
        return 0;
}
 
void print_in_middle(WINDOW *win, int starty, int startx, int width, char *string, chtype color)
{       int length, x, y;
        float temp;
 
        if(win == NULL)
               win = stdscr;
        getyx(win, y, x);
        if(startx != 0)
               x = startx;
        if(starty != 0)
               y = starty;
        if(width == 0)
               width = 80;
 
        length = strlen(string);
        temp = (width - length)/ 2;
        x = startx + (int)temp;
        wattron(win, color);
        mvwprintw(win, y, x, "%s", string);
        wattroff(win, color);
        refresh();
}

Validarea Câmpurilor

În mod normal un câmp va accepta orice date introduse de utilizator. E posibil să ataşaţi validare de un câmp. Apoi orice încercare de a părăsi câmpul, în timp ce conţine date ce nu corespund validării va eşua. Câteva tipuri de vaildări validează fiecare character care e introdus într-un câmp.

Validarea poate fi ataşată de un câmp în modul următor.

int set_field_type(FIELD *field,          /* câmpul de modificat */
           FIELDTYPE *ftype,      /* tipul de asociat */
           ...);                  /* argumente adiţionale*/

Odată setată validarea poate fi găsită cu

FIELDTYPE *field_type(FIELD *field);      /* câmpul */

Driverul form validează datele din formular numai când sunt introduce de utilizator. Validarea nu se petrece atunci când

  • Aplicaţia schimbă conţinutul câmpului apelând funcţia set_field_buffer.
  • Valorile legate ale câmpurilor sunt schimbate indirect – schimbând câmpul de care sunt legate

Următoarele sunt tipuri predefinite de validare. Puteţi specifica validare customizată, deşi e un pic cam complicat.

TYPE_ALPHA

Acest tip de câmp acceptă date alfabetice; fără spaţii, numere, caractere speciale (se verifică când se introduce caracterul). E activate cu:

int set_field_type(FIELD *field,          /* câmpul de modificat */
                   TYPE_ALPHA,            /* tipul de asociat */
                  int width);            /* lungimea maximă a câmpului */

Argumentul width setează lungimea minimă a datelor. Utilizatorul trebuie să introducă măcar un număr egal cu width de caractere ca să poată părăsi câmpul. Normal veţi dori să setaţi acest parametru la lăţimea câmpului; dacă e mai mare decât lăţimea câmpului, validarea va eşua întotdeauna. O lungime minimă de 0 va face completarea câmpului opţională.

TYPE_ALNUM

Acest tip de câmp acceptă date alfabetice şi cifre; fără spaţii, caractere speciale (se verifică când se introduce caracterul). E activate cu:

int set_field_type(FIELD *field,          /* câmpul de modificat */
                  TYPE_ALNUM,            /* tipul de asociat */
                  int width);            /* lungimea maximă a câmpului */

Argumentul width setează lungimea minimă a datelor. La fel ca pentru TYPE_ALPHA, Normal veţi dori să setaţi acest parametru la lăţimea câmpului; dacă e mai mare decât lăţimea câmpului, validarea va eşua întotdeauna. O lungime minimă de 0 va face completarea câmpului opţională.

TYPE_ENUM

Acest tip vă permite să restricţionaţi valorile pe care un câmp le poate lua (de exemplu, codul poştal din două litere). Se setează cu:

int set_field_type(FIELD *field,          /* câmpul de modificat */
                  TYPE_ENUM,             /* tipul de asociat */                char **valuelist;      /* list of possible values */
                  int checkcase;         /* case-sensitive? */
                  int checkunique);      /* trebuie specificat unic? */

Parametrul valuelist trebuie să pointeze la o listă de stringuri terminată cu NULL. Argumentul checkcase, dacă e true, face comparaţia cu şirurile case-sensitive.

Când utilizatorul părăseşte un camp de tip TYPE_ENUM, procedura de validare încercă să completeze data din buffer la o dată validă. Dacă s-a completat un şir correct atunci nu se mai realizează nimic. Dar este deasemeni posibil să se completeze un prefix al unui şir şi va fi completat şirul corect.

Normal, dacă introduceţi un astfel de prefix şi se potriveşte cu mai multe şiruri din listă, prefixul va fi completat până la prima valoare ce se potriveşte. Dar argumentul checkunique, dacă e true, cere ca prefixele să fie unice pentru ca să se completeze.

Cererile de intrare REQ_NEXT_CHOICE şi REQ_PREV_CHOICE pot fi folositoare pentru aceste câmpuri.

TYPE_INTEGER

Acest tip de câmp acceptă date de tip  integer. Se setează cu:

int set_field_type(FIELD *field,          /* câmpul de modificat */
                  TYPE_INTEGER,          /* tipul de asociat */
                  int padding,           /* # places to zero-pad to */
                  int vmin, int vmax);   /* intervalul de validitate */

Caracterele valide sunt cifre cu un minus opţional la început. Intervalul e verificat la ieşire. Dacă maximumul intervalului e mai mic sau egal cu cu minimul atunci intervalul e ignorat.

Dacă valoare e înafara intervalului, e compleată cu zerouri în faţă ca să respecte paddingul.

O valoare de tip TYPE_INTEGER poate fi transformată cu funcţia atoi(3) din C.

TYPE_NUMERIC

Acest tip de câmp acceptă date de tip real. It is set up as follows:

int set_field_type(FIELD *field,          /* câmpul de modificat */
                  TYPE_NUMERIC,          /* tipul de asociat */
                  int padding,           /* # places of precision */
                  int vmin, int vmax);   /* intervalul de validitate */

Caracterele valide sunt cifre cu un minus opţional la început, posibil conţinând un punct zecimal. Intervalul e verificat la ieşire. Dacă maximumul intervalului e mai mic sau egal cu cu minimul atunci intervalul e ignorat.

Dacă valoare e înafara intervalului, e compleată cu zerouri la capăt ca să respecte paddingul.

O valoare de tip TYPE_INTEGER poate fi transformată cu funcţia atof(3) din C.

 

TYPE_REGEXP

Acest tip de camp acceptă date ce respectă o expresie regulată. E setat astfel:

int set_field_type(FIELD *field,          /* câmpul de modificat */
                   TYPE_REGEXP,           /* tipul de asociat */
                  char *regexp);         /* expresia de respectat */

Sintaxa pentru expresiile regulate este acea din regcomp(3). Verificarea se face la ieşire.

Driverul Form: Baza sistemului de formulare

Ca şi în sistemul menu, form_driver() joacă un rol foarte important în sistemul form. Toate tipurile de cereri către sistemul form ar trebui trecute prin form_driver().

int form_driver(FORM *form,    /* formularul asupra căruia operăm    */
               int request)   /* codul cererii       */

După cum aţi văzut în exemplele de mai sus, trebuie să fiţi într-o buclă ce aşteaptă inputul utilizatorului şi apoi să decideţi dacă sunt date sau cereri. Cererile apoi sunt trimise spre form_driver() ca să facă acţiunea necesară.

Aceste cereri pot fi împărţite în categorii. Câteva cereri şi utilizarea lor sunt explicate mai jos:

Cereri de navigare în pagină

Aceste cereri cauzează mutări ale paginii în formular, declanşând afişarea unui nou formular. Un formular poate conţine mai multe pagini. Dacă aveţi un formular mare cu multe câmpuri şi secţiuni logice atunci puteţi împărţi formularul în pagini. Funcţia set_new_page() e utilizată pentru a seta o nouă pagină la câmpul specificat.

int set_new_page(FIELD *field,                /* Câmpul la care începe sau se termină o pagină */
                bool new_page_flag);  /* TRUE pentru a pune un break */

Următoarele cereri permit mutarea pe alte pagini

  • REQ_NEXT_PAGE

Mutare pe pagina următoare.

  • REQ_PREV_PAGE

Mutare pe pagina precedentă.

  • REQ_FIRST_PAGE

Mutare pe prima pagină.

  • REQ_LAST_PAGE

Mutare pe ultima pagină.

Aceste cereri tratează lista ca fiind circulară; adică, REQ_NEXT_PAGE apelată de pe ultima pagină ne va duce pe prima, şi REQ_PREV_PAGE de pe prima ne va duce pe ultima.

Cereri de navigare între câmpuri

Aceste cereri se ocupă de navigarea între câmpurile de pe aceeaşi pagină.

  • REQ_NEXT_FIELD

Mutare la următorul câmp.

  • REQ_PREV_FIELD

Mutare la câmpul precedent.

  • REQ_FIRST_FIELD

Mutare pe primul câmp.

  • REQ_LAST_FIELD

Mutare pe ultimul câmp.

  • REQ_SNEXT_FIELD

Mutare pe următorul câmp sortat.

  • REQ_SPREV_FIELD

Mutare pe câmpul anterior sortat.

  • REQ_SFIRST_FIELD

Mutare pe primul câmp sortat.

  • REQ_SLAST_FIELD

Mutare pe ultimul câmp sortat.

  • REQ_LEFT_FIELD

Mutare la stânga în câmp.

  • REQ_RIGHT_FIELD

Mutare la dreapta în câmp.

  • REQ_UP_FIELD

Mutare sus în câmp.

  • REQ_DOWN_FIELD

Mutare jos în câmp.

Aceste cereri tratează lista de câmpuri ca circulară; adică, REQ_NEXT_FIELD apelată de pe ultimul câmp ne va duce pe primul, şi REQ_PREV_FIELD apelată de pe primul câmp ne va duce pe ultimul. Ordinea câmpurilor pentru aceste cereri (şi REQ_FIRST_FIELD şi REQ_LAST_FIELD) e pur şi simplu ordinea pointerilor în tabloul formularului (setaţi cu new_form() sau set_form_fields()

Este deasemeni posbil să parcurgeţi câmpurile ca şi cum ar fi fost sortate în ordinea ecranului, aşa încât vom avea de la stânga la derapta şi de sus în jos. Pentru a face acest lucru, ultilizaţi al doilea grup de patru cereri.

În sfârşit, e posibil să navigaţi între câmpuri utilizând direcţiile sus, jos, dreapta, şi stânga. Pentru a face acest lucru, ultilizaţi al treilea grup de patru cereri. Atenţie, totuşi, că poziţia unui formular pentru aceste cereri este colţul din stânga sus.

De exemplu, să presupunem că aveţi un câmp multilinie B, şi două câmpuri cu o linie A şi C pe aceeaşi linie cu B, cu A în stânga lui B şi C în dreapta lui B. O cerere REQ_MOVE_RIGHT de la A va merge la B numai dacă A, B, şi C au aceeaşi primă linie; altfel va trece peste B la C.

Cereri de navigare în câmpuri

Aceste cereri se ocupă de navigarea în cursorului de editare într-un câmp.

  • REQ_NEXT_CHAR

Mutare la următorul caracter.

  • REQ_PREV_CHAR

Mutare la caracterul precedent.

  • REQ_NEXT_LINE

Mutare la următoarea linie.

  • REQ_PREV_LINE

Mutare la linia precedentă.

  • REQ_NEXT_WORD

Mutare la cuvântul următor.

  • REQ_PREV_WORD

Mutare la cuvântul precedent.

  • REQ_BEG_FIELD

Mutare la începutul câmpului.

  • REQ_END_FIELD

Mutare la sfârşitul câmpului.

  • REQ_BEG_LINE

Mutare la începutul liniei.

  • REQ_END_LINE

Mutare la sfârşitul liniei.

  • REQ_LEFT_CHAR

Mutare la stânga în câmp.

  • REQ_RIGHT_CHAR

Mutare la dreapta în câmp.

  • REQ_UP_CHAR

Mutare în sus în câmp.

  • REQ_DOWN_CHAR

Mutare în jos în câmp.

Fiecare cuvânt e separate de cel anterior şi de cel următor prin spaţiu. Comenzile pentru mutarea la îneputul şi sfârşitul linie sau câmpului caută prima apariţie a unui character non printabil.

Cereri de Scroll

Câmpurile care sunt dinamice şi au crescut peste dimenisunea vizibilă şi câmpurile create explicit cu rânduri offscreen sunt scrollabile. Câmpurile cu o linie se scrolează orizontal; Câmpurile cu mai multe linii se scrolează vertical. Majoritatea cererilor de scroll sunt declanşate de editarea unei mutări în interiorul câmpului (librăria scrollează câmpul pentru a menţine vizibilitatea cursorului). Se pot folosi următoarele cereri pentru scrolling:

  • REQ_SCR_FLINE

Scrolează vertical o linie înainte.

  • REQ_SCR_BLINE

Scrolează vertical o linie înapoi.

  • REQ_SCR_FPAGE

Scrolează vertical o pagină înainte. 

  • REQ_SCR_BPAGE

Scrolează vertical o pagină înapoi.

  • REQ_SCR_FHPAGE

Scrolează vertical o jumătate de pagină înainte.

  • REQ_SCR_BHPAGE

Scrolează vertical o jumătate de pagină înapoi.

  • REQ_SCR_FCHAR

Scrolează orizontal un caracter înainte.

  • REQ_SCR_BCHAR

Scrolează orizontal un caracter înapoi.

  • REQ_SCR_HFLINE

Scrolează orizontal o lungime de câmp înainte.

  • REQ_SCR_HBLINE

Scrolează orizontal o lungime de câmp înapoi.

  • REQ_SCR_HFHALF

Scrolează orizontal o jumătate de lungime de câmp înainte.

  • REQ_SCR_HBHALF

Scrolează orizontal o jumătate de lungime de câmp înapoi. 

Din motive de scrolling, o pagină dintr-un camp este înălţimea lui.

Cereri de editare

Când trimiteţi driverului form un caracter ASCII e tratat ca o cerere de adăugare a acelui carater la bufferul de date al câmpului respectiv. Dacă aceasta este o inserare sau înlocuire depinde de modul de editare a câmpului respectiv (inserarea e modul iniţial).

Următoarele cereri sunt pentru editarea câmpului şi schimbării modului de editare:

  • REQ_INS_MODE

Setează modul de inserare.

  • REQ_OVL_MODE

Setează modul overlay.

  • REQ_NEW_LINE

Cerere pentru linie nouă (vedeţi nai jos explicaţiile).

  • REQ_INS_CHAR

Inserează un spaţiu la locaţia cursorului.

  • REQ_INS_LINE

Inserează o linie goală la locaţia cursorului.

  • REQ_DEL_CHAR

Şterge caracterul de la cursor.

  • REQ_DEL_PREV

Şterge cuvântul din înaintea  cursorului.

  • REQ_DEL_LINE

Şterge linia de la cursor.

  • REQ_DEL_WORD

Şterge cuvântul de la cursor.

  • REQ_CLR_EOL

Şterge sfârşit de linie.

  • REQ_CLR_EOF

Şterge sfârşit de câmp.

  • REQ_CLEAR_FIELD

Şterge sfârşit întregul câmp.

Comportamentul cererilor REQ_NEW_LINE şi REQ_DEL_PREV e complicat şi e controlat în mare de nişte opţiuni din formulare. Cazurile speciale sunt declanşate atunci când cursorul e la începutul unui câmp sau pe ultima linie a lui.

Mai întâi să luăm REQ_NEW_LINE:

Comportamentul normal a lui REQ_NEW_LINE, în modul inserare, este să despartă linia curentă de la cursorul de editare, inserând bucata de linie de după cursor ca o linie nouă şi mută cursorul la începutul acestei linii (puteţi să vă gândiţi că se inserează o linie nouă în bufferul câmpului).

Comportamentul normal a lui REQ_NEW_LINE, în modul overlay, este să ştaergă linia pe care este cursorul de editare. Curosrul este mutat apoi la începutul liniei următoare.

Totuşi, REQ_NEW_LINE dat la începutul unui câmp, sau pe ultima linie a lui, va realiza  REQ_NEXT_FIELD. Dacă O_NL_OVERLOAD e deactivată, această acţiune e deactivată.

Acum să ne uităm la REQ_DEL_PREV:

Comportamentul normal a lui REQ_DEL_PREV este să şteargă caracterul precedent. Dacă modul inserare e activat, şi cursorul e la începutul unei linii, şi textul de pe acea linie va încape pe linia precedentă, va lipi conţinutul liniei curente la cea precedentă şi va şterge linia curentă (vă puteţi gândi că şterge o lini nouă din bufferul câmpului).

Totuşi, REQ_DEL_PREV dat la începutul unui camp e tratat ca  REQ_PREV_FIELD.

Dacă opţiunea O_BS_OVERLOAD e deactivată, această acţiune specială e deactivată şi driverul form returnează E_REQUEST_DENIED.

Cereri de Ordine

Dacă tipul câmpului dumneavoastră e oronat, şi are funcţii associate pentru a lua valorile următoare şi precedente de un tip de la o valoare dată, există cereri care pot lua valoarea din buffer:

  • REQ_NEXT_CHOICE

Plasează valoarea succesorului valorii curente în buffer.

  • REQ_PREV_CHOICE

Plasează valoarea predecesorului valorii curente în buffer.

Dintre tipurile predefinite numai TYPE_ENUM are funcţii de successor şi predecessor definite. Când definiţi un tip de câmp  (vedeţi Tipuri Customizate), puteţi asocial propriile funcţii de ordonare.

Comenzile Aplicaţiei

Cererile formularelor sunt reprezentate ca întregi mai mari ca valoarea maximă din curses KEY_MAX şi mai mici sau egale cu constanta MAX_COMMAND. O valoare ce nu este din acest interval este ignorată de form_driver(). Aşadar acest lucru poate fi folosit î orice scop de aplicaţie. Poate fi tratat ca o acţiune specifică a aplicaţiei şi să facă acţiunile corespunzătoare.

 

Librăria de instrumente

Acum că aţi văzut care sunt capabilitătile sistemului ncurses şi a librăriilor adiacente, vă pregătiţi să faceţi un proiect care foloseşte ecranul la maxim. Dar aşteptaţi.. E destul de dificil de scis şi întreţinut un GUI în ncurses simplu şi chiar cu librăriile adiacente. Există librării gata create de controale care pot fi folosite în loc să scrieţi dumneavoastră propriile controale. Puteţi folosi câteva dintre ele, să vă uitaţi peste cod, sau chiar să le extindeţi.

CDK (Curses Development Kit)

În cuvenitele autorului

CDK stands for 'Curses Development Kit' and it currently contains 21 ready to use widgets which facilitate the speedy development of full screen curses programs.

Kitul oferă câteva controale utile, care pot fi folosite direct în programele voastre . E destul de bine scris şi documentaţia este foarte bună. Exemplele din directorul exemple poate fi un loc bun de pornit pentru începători. CDK poate fi downladat de la http://www.vexus.ca/release/cdk.tar.gz. Urmaţi instrucţiunile din fişierul README pentru a-l instala.

Lista de controale

Urmează lista de controale din cdk şi descrierea lor.

Tipul controlului     Scurtă descriere
===========================================================================
Alphalist             Permite utilizatorului să aleagă dintr-o listă de cuvinte, cu abilitatea de a micşora lista de căutare tipărind câteva caractere din cuvântul dorit.
 
Buttonbox             Acesta creează un control multi buton. 
 
Calendar              Creează un control calendar.
 
Dialog                Îi apare utilizatorului un mesaj, şi acesta
                      poate allege un răspuns din acele date.
 
Entry                 Îi permite utilizatorului să introducă diverse tipuri de informaţii.
 
File Selector         Un selector de fişiere construit din controale Cdk. Acest exemplu vă arată cum să construiţi controale mai complicate folsind librăria CDK.
 
Graph                 Desenează un grafic.
 
Histogram             Desenează o histogramă.
 
Item List             Creates un câmp popup ce permite utilizatorului să selecteze o opţiune din mai multe. Foarte util pentru zilele săptămânii sau pentru luni.
 
Label                 Afişează mesaje într-o boxă pop up, sau labelul poate fi considerat parte a ecranului.
 
Marquee               Afişează un mesaj într-un scrolling marquee.
 
Matrix                Creează o matrice complexă cu o mulţîme de opţiuni.
 
Menu                  Creează un meniu pull-down.
 
Multiple Line Entry   Un câmp multilinie. Foarte util pentru câmpuri mari.(ca un câmp de descriere)
 
Radio List            Creează o listă de butoane radio.
 
Scale                 Creează o scală numerică. Folosită pentru a permite utilizatorului să aleagă o valoare numerică şi să restrângă valorile la un anumit interval.
 
Scrolling List        Creează o listă/meniu scrollabil.
 
Scrolling Window      Creează un camp de vizualizare scrollabil. Poate adăuga informaţii în câmp în timpul afişării. 
E bine de folosit pentru a arăta progresul cuiva. (la fel ca o consolă)
 
Selection List        Creează o listă de selecţie cu opţiuni multiple.
 
Slider                La fel ca şi controlul Scale, acest control oferă
                      o bară pentru a vizualiza o scală numerică.
 
Template              Creează un camp de intrare cu poziţii sensibile la caractere. Folosite pentru câmpuri preformatate ca data sau număr de telefon.
        
Viewer                Aceste este un vizualizator de fişiere/ informaţii. Foarte util când trebuie să afişaţi foarte multe informaţii
===========================================================================

Câteva facilităţi atractive

Pe lângă faptul că ne fac viaţa mai uşoară cu controlae deja făcute, cdk rezolvă o probemă fustrantă legată de afişarea şirurilor cu mai multe culori, şi a şirurilor justificate. Taguri speciale de formatare pot fi introduce în şiruri care sunt transmise funcţilor CDK. De exemplu:

Dacă şirul e

"</B/1>This line should have a yellow foreground and a blue background.<!1>"

dat ca parametru funcţiie newCDKLabel(), va afişa linia cu foreground galben şi background albastru. Există şi alte taguri pentru justificarea şirurilor, introducerea de caractere speciale etc.. Vedeţi în pagina man cdk_display(3X). Pagina man oferă detalii şi exemple.

Concluzie

În concluzie, CDK e unpachet bine scris de controale, care dacă e utilizat bine poate fi utilizat pentru a creea GUI complexe.

Dialogul

Cu mult timp în urmă, în Septembrie 1994, când puţini oameni ştiau linux, Jeff Tranter a scris un articol despre dialog în Linux Journal. Articolul începe cu cuvintele de mai jos..

Linux is based on the Unix operating system, but also features a number of unique and useful kernel features and application programs that often go beyond what is available under Unix. One little-known gem is "dialog", a utility for creating professional-looking dialog boxes from within shell scripts. This article presents a tutorial introduction to the dialog utility, and shows examples of how and where it can be used

După cum explică el, dialogul este o piatră nepreţuită în crearea cu uşurinţă de boxe de dialog frumoase. Creează o mulţime de boxe, meniuri, liste de check etc.. De obicei se instalează iniţial. Dacă nu, puteţi să-l găsiţi la ibiblio linux archive.

În articolul de mai sus se explică foarte bine funcţionalitatea dialogului. Pagina man are mai multe detalii. Poate fi folosit într-o mulţime de situaţii. Un exemplu este construirea kerenlului linux în mod text. Kernelul Linux foloseşte o altă versiune de dialog.

dialog a fost iniţial conceput pentru a fi folosit cu shellurile. Dacă vreţi să-l folosiţi în programele c, atunci puteţi folosi libdialog. Documentaţia privind acest lucru e destul de rară. Puteţi afla mai multe din fişierul header dialog.h care vine cu librăria. Ar trebui să faceţi câteva modificări aici şi acolo pentru a găsi ce aveţi nevoie. Sursa este uşor de modificat. Am folosit-o de câteva ori modificând sursa.

Modulele Curses Perl CURSES::FORM şi CURSES::WIDGETS

Modulul Curses perl, Curses::Form şi Curses::Widgets dau acces la Curses din perl. Dacă aveţi curses şi perl instalate, puteţi lua acest modul de la CPAN All Modules page. Luaţi cele trei module arhivate din categoria Curses. Odată instalate puteţi folosi aceste module din scripturile perl ca orice alt modul. Pentru mai multe informaţii desore modulele perl vedeti pagina man perlmod. Modulele de mai sus vin cu documentaţie şi au şi undele scripturi demonstrative pentru a testa funcţionalitatea. Deşi controalele oferite sunt foarte rudimentare, aceste modul oferă un acces binevenit la curses din perl.

Pentru mai multe informaţii vedeţi paginile man Curses(3) , Curses::Form(3) şi Curses::Widgets(3). Aceste pagini sunt instalate doar atunci când aveţi instalate modulele menţionate.

Doar pentru distracţie !!!

Aceste secţiuni conţin câteva programe scrise de mine doar pentru distracţie. Nu au nici o semnificaţie pentru programarea mai bună sau utilizarea lui ncurses. Sunt oferite aici pentru a da idei începătorilor şi pentru ca alţii să pună şi ei alte program aici. Dacă aţi scris programe în curses şi vreţi să le publicaţi aici contactaţi-.

Jocul vieţii este o minune a matematicii. În cuvintele lui Paul Callahan

 
The Game of Life (or simply Life) is not a game in the conventional sense. There are no players, and no winning or losing. Once the "pieces" are placed in the starting 
position, the rules determine everything that happens later. Nevertheless, Life is full of surprises! In most cases, it is impossible to look at a starting position (or pattern) and see what will happen in the future. The only way to find out is to 
follow the rules of the game.
 

Acest program începe cu un patern U inversat şi arată ce frumoasă e viaţa. Mai sunt multe de făcut în acest program. Puteţi lăsa utilizatorul să introducă paternal lui sau chiar să citească dintr-un fişier. Deasemeni puteţi lăsa utilizatorul să schimbe regulile. Căutaţi pe google lucruri interesante despre jocul vieţii.

File Path: JustForFun/life.c

Pătratul magic, o altă minune a matematicii, e foarte simplu de înţeles dar foarte greu de realizat. Într-un pătrat magic suma tuturor numerelor de pe fiecare rând, coloană sunt egale. Chiar şi sumele de pe diagonale pot fi egale. Sunt multe versiuni fiecare cu alte proprietăţi.

Acest program creează un pătrat ordonat la întâmplare.

File Path: JustForFun/magic.c

Faimoasa problemă a turnurilor din hanoi. Scopul jocului este de a muta discurile de pe prima tijă pe ultima, folosind tija din mijloc. Problema este să nu plasaţi un disc mai mare peste unul mai mic.

File Path: JustForFun/hanoi.c

Faimoasa problemă a celor N-Regine. Trebuie să aşezaţi N regine pe o tablă N X N fără să se atace între ele.

Această problemă e rezolvată cu ajutorul tehnicii backtarcking.

File Path: JustForFun/queens.c

Un joc distractiv.

File Path: JustForFun/shuffle.c

Un simplu tutorial de tipărire, l-am creat mai mult de nevoie. Dacă ştiţi cum să puneţi degetele correct pe tastatură, dar vă lipseşte practica, acesta vă poate ajuta.

File Path: JustForFun/tt.c


Copyright and Maintained by Pradeep Padala