Precedente Successivo Indice

5. La libreria Form

La libreria form è una estensione delle curses che supporta una facile programmazione di maschere video per l'inserimento di dati e un facile controllo del programma.

La libreria form apparve la prima volta nel System V della AT&T. La versione qui documentata è il codice freeware della form distribuito con le ncurses.

5.1 Compilare con la Libreria form

I vostri moduli che utilizzino la form devono importare le dichiarazioni della libreria form con


#include <form.h>

e dovrà essere esplicitamente linkato alla libreria form utilizzando l'argomento -lform. Notate che dovono anche linkare la libreria ncurses mediante -lncurses. Molti moderni linker sono a due passaggi ed accettano qualunque ordine, ma è buona pratica mettere -lform prima e -lncurses dopo.

5.2 Panoramica delle Form

Una form è una collezione di campi: ciascun campi può essere sia un'etichetta (testo visualizzato) oppure un luogo per l'inserimento di dati. Grandi form possono venire suddivise in pagine; il passaggio ad un'altra pagina pulisce lo schermo. Per avere form, create gruppi di campi e connetteteli con oggetti riquadro di form; la libreria form vi rende ciò relativamente semplice.

Una volta definita, una form può essere pubblicata, cioè scritta nella finestra associata. Attualmente, ogni form ha due finestre associate; una finestra contenitore in cui il programmatore può scribacchiare titoli e bordi, ed una sottofinestra in cui i campi della form vengono visualizzati.

Mentre l'utente riempie la form pubblicata, comandi di navigazione supportano spostamenti tra campi, comandi di editazione supportano modifiche all'interno dei campi, e semplice testo aggiunge o modifica i dati nel campo corrente. La libreria form (il designer) vi permette di legare ogni comando di navigazione e di editazione a qualunque tasto riconosciuto dalle curses.

I campi possono avere condizioni di validazione imposte per controllare i dati d'input per tipo e valore. La libreria form supporta un ricco insieme di tipi di campo predefiniti, e rende relativamente facile definirne di nuovi.

Appena la transazione è completata (o annullata), una form può venir spubblicata (cioè de-visualizzata), ed infine la memoria associata ad essa ed ai suoi campi lasciata disponibile al riutilizzo.

Il flusso generico del controllo di una form somiglia a questo:

  1. Inizializzazione delle curses.
  2. Creazione dei campi della form mediante new_field().
  3. Creazione della form mediante new_form().
  4. Pubblicazione della form mediante form_post().
  5. Rinfresco dello schermo.
  6. Elaborazione delle richieste dell'utente tramite un ciclo di input.
  7. Spubblicazione della form mediante form_unpost().
  8. Rilascio della form tramite free_form().
  9. Rilascio dei campi tramite free_field().
  10. Chiusura delle curses.

Notate come questo somigli molto ad un programma menù: la libreria form gestisce compiti simili sotto diversi aspetti, e la sua interfaccia è stata ovviamente progettata per assomigliare alla libreria menù laddove possibile.

Nei programmi form, comunque, la ''gestione delle richieste dell'utente'' è in ogni modo più complicata. Oltre alle operazioni di navigazione simili al menù, il ciclo di gestione del menù deve supportare l'editazione dei campi e la validazione dei dati.

5.3 Creazione e Rilascio di Campi e Form

La funzione base per creare campi è la new_field():

FIELD *new_field(int height, int width,   /* dimensioni del campo */ 
                 int top, int left,       /* angolo superiore sinistro */
                 int offscreen,           /* numero di righe fuori-schermo */
                 int nbuf);               /* numero di buffer di lavoro */

Le voci di un menù occupano sempre una sola riga, ma i campi di una form possono occupare più righe. Così new_field() vi richiede di specificare larghezza ed altezza (i primi due argomenti, che devono essere entrambi maggiori di zero).

Dovete inoltre specificare la locazione dell'angolo superiore sinistro del campo sullo schermo (il terzo ed il quarto argomento, che devono valere zero o più). Notate che queste coordinate sono relative alla sottofinestra della form, che coincide inizialmente con stdscr, ma non è necessariamente stdscr se avete chiamato esplicitamente la set_form_window().

Il quinto argomento vi permette di specificare un numero di righe fuori schermo. Se questo è zero, l'intero campo sarà sempre visualizzato. Se non è zero, l'intera form diverrà scrollabile, con solo uno schermo intero (inizialmente la parte superiore) visualizzato in ogni momento. Se voi create un campo dinamico e lo fate crescere in modo che non stia più nello schermo, la form diverrà scrollabile anche se l'argomento fosse zero.

La libreria form alloca un buffer di lavoro per ogni campo: la dimensione di ciascun campo è data da ((altezza + fuori-schermo)*larghezza + 1, un carattere per ogni posizione nel campo più un terminatore NULL. Il sesto argomento è il numero di buffer di lavoro addizionali da allocare per il campo: la vostra applicazione può usarli per i suoi scopi.

FIELD *dup_field(FIELD *field,            /* campo da copiare */
                 int top, int left);      /* posizione della nuova copia */

La funzione dup_field() duplica un campo esistente in una nuova posizione. Dimensioni e dati nei buffer vengono copiati; alcuni attributi e bit di stato no (vedi la form_field_new(3X) dettagli).

FIELD *link_field(FIELD *field,            /* campo da copiare */
                 int top, int left);      /* posizione della nuova copia */

Anche la funzione link_field() duplica un campo esistente in una nuova posizione. La differenza colla dup_field() è che fa sì che il buffer del nuovo campo sia in comune col vecchio.

Oltre all'ovvio utilizzo nel creare un campo editabile in due differenti pagine di una form, campi linkati vi danno un modo di creare etichette dinamiche. Se dichiarate parecchi campi linkati ad un originale, e li rendete inattivi, cambiamenti nell'originale si propagheranno agli altri campi linkati.

Come i campi duplicati, anche i campi linkati hanno bit di attributi separati dall'originale.

Come potete immaginare, tutte queste allocazioni ritornano NULL se la allocazione non è possibile a causa di un errore di memoria o di argomenti fuori limiti.

Per connettere i campi ad una form, usate

FORM *new_form(FIELD **fields);

Questa funzione si aspetta un vettore (terminato da NULL) di puntatori a campi. Detti campi sono connessi ad un oggetto form di nuova allocazione; viene ritornato il suo indirizzo (o NULL se la creazione fallisce).

Notate che la new_field() non copia il vettore in memoria interna; se voi ne modificate il contenuto durante l'elaborazione della form, possono accadere un sacco di cose bizzarre. Inoltre notate che ogni campo dato può essere connesso ad una sola form. Le funzioni free_field() e free_form sono disponibili per rilasciare campi ed oggetti form. È un errore cercare di rilasciare un campo connesso ad una form, ma non vice-versa; perciò generalmente rilascerete per prime le vostre form.

5.4 Cattura e Modifica degli Attributi di Campi

Ogni campo di una form ha un certo numero di attributi posizione e dimensione associati ad esso. Ci sono altri attributi del campo usati per controllare la visualizzazione e l'editazione del campo. Alcuni (ad esempio, il bit O_STATIC ) implica sufficienti complicazioni da necessitare di loro proprie sezioni più oltre. Qui ci occupereremo delle funzioni usate per interagire con parecchi attributi di base.

Quando si crea un campo, gli attributi non specificati dalla funzione new_field vengono ricopiati da un campo invisibile di riferimento. Nelle funzioni di cambio e cattura degli attributi un argomento NULL intende fare riferimento a questo campo. Cambiamenti in esso sono permanenti sino alla terminazione dell'applicazione.

Cattura dei Dati di Dimensione e Locazione

Potete prelevare dimensioni e locazioni di un campo attraverso:

int field_info(FIELD *field,              /* campo dal quale prelevare */
               int *height, *int width,   /* dimensioni del campo */ 
               int *top, int *left,       /* angolo superiore sinistro */
               int *offscreen,            /* numero di righe fuori-schermo */
               int *nbuf);                /* numero di buffer di lavoro */

Questa funzione è una specie di inverso della new_field(); invece di settare dimensione e locazione di un nuovo campo, lei li cattura da uno esistente.

Cambiare la Locazione di un Campo

È possibile spostare la posizione di un campo sullo schermo:

int move_field(FIELD *field,              /* campo da spostare */
               int top, int left);        /* nuovo angolo superiore sinistro */

Potete, naturalmente, vedere la locazione corrente attraverso la field_info().

L'Attributo Giustificazione

I campi di una riga possono essere senza-giustificazione, giustificati a destra, a sinistra o centrati. Ecco come manipolare questo attributo:

int set_field_just(FIELD *field,          /* campo da modificare */
                   int justmode);         /* modalità da impostare */

int field_just(FIELD *field);             /* cattura la modalità del campo */

I valori modalità accettati e ritornati da questa funzione sono le macro NO_JUSTIFICATION, JUSTIFY_RIGHT, JUSTIFY_LEFT, o JUSTIFY_CENTER.

Attributi di Visualizzazione del Campo

Per ciascun campo, potete impostare un attributo di primo piano per i caratteri inseriti, un attributo di sottofondo per l'intero campo, ed un carattere riempitivo per le porzioni vuote del campo. Potete inoltre controllare la paginazione della form.

Questo gruppo di quattro attributi di campo controllano l'aspetto visivo del campo sullo schermo, senza influenzare in alcun modo i dati nel buffer del campo.

int set_field_fore(FIELD *field,          /* campo da modificare */
                   chtype attr);          /* attributo da impostare */ 

chtype field_fore(FIELD *field);          /* campo da interrogare */

int set_field_back(FIELD *field,          /* campo da modificare */
                   chtype attr);          /* attributo da impostare */ 

chtype field_back(FIELD *field);          /* campo da interrogare */

int set_field_pad(FIELD *field,           /* campo da modificare */
                 int pad);                /* carattere riempitivo da impostare */ 

chtype field_pad(FIELD *field);

int set_new_page(FIELD *field,            /* campo da modificare */
                 int flag);               /* TRUE per forzare una nuova pagina */ 

chtype new_page(FIELD *field);            /* campo da interrogare */

Gli attributi impostati e ritornati dalle prime quattro funzioni sono i normali attributi video delle curses(3x) (A_STANDOUT, A_BOLD, A_REVERSE ecc.).

Il bit di pagina di un campo controlla se la sua visualizzazione avviene all'inizio di una nuova schermata di form.

Bit di Opzione di Campo

C'è anche una vasta collezione di bit-opzione di campo che potete impostare per controllare vari aspetti dell'elaborazione delle form. Potete manipolarle con queste funzioni:

int set_field_opts(FIELD *field,          /* campo da modificare */
                   int attr);             /* attributi da impostare */ 

int field_opts_on(FIELD *field,           /* campo da modificare */
                  int attr);              /* attributi da attivare */ 

int field_opts_off(FIELD *field,          /* campo da modificare */
                   int attr);             /* attributi da disattivare */ 

int field_opts(FIELD *field);             /* campo da interrogare */

Inizialmente, tutte le opzioni sono attive. Questi sono i bit-opzione disponibili:

O_VISIBLE

Controlla se il campo è visibile sullo schermo. Può essere usato durante l'elaborazione della form per nascondere o visualizzare dei campi in base al valore di altri campi.

O_ACTIVE

Controlla se il campo è attivo durante l'elaborazione della form (per esempio visitabile mediante i comandi di navigazione). Può essere usato per creare etichette o campi derivati con buffer modificabili dalla applicazione, non dall'utente.

O_PUBLIC

Controlla se i dati sono visualizzati durante l'inserimento nel campo. Se questa opzione è spenta su un campo, la libreria accetterà dati in quel campo, ma non vi sarà visualizzazione ed il cursore non si muoverà. Potete disattivare il bit O_PUBLIC per i campi password.

O_EDIT

Controlla se i dati nel campo possono essere modificati. Quando questa opzione è disabilitata, tutte le richieste di editing eccetto REQ_PREV_CHOICE e REQ_NEXT_CHOICE falliranno. Tali campi di sola lettura possono essere utili per messaggi di help.

O_WRAP

Controlla l'a-capo nei campi multi linea. Normalmente, quando un carattere di una parola (separata da spazi) raggiunge la fine della riga corrente, l'intera parola viene spostata alla linea successiva (ammesso che ve ne sia una). Quando questa opzione è disattivata, la parola verrà spezzata alla fine della linea.

O_BLANK

Controlla lo sbiancamento del campo. Quando questa opzione è attiva, lo scrivere un carattere nella prima posizione del campo provoca la cancellazione dell'intero campo (eccetto il carattere appena inserito).

O_AUTOSKIP

Controlla il salto automatico al campo successivo quando un campo è pieno. Normalmente, quando l'utente prova ad inserire più dati di quanti ne possano stare nel campo, il punto di editazione passa nel campo successivo. Quando questa opzione è disattivata il cursore dell'utente si blocca alla fine del campo. Questa opzione è ignorata nei campi dinamici che non hanno raggiunto la loro dimensione massima.

O_NULLOK

Controlla se la validazione si applica al campo vuoto. Normalmente non lo è; l'utente può lasciare il campo vuoto senza che venga eseguita l'usuale validazione all'uscita. Se questa opzione è spenta su un campo, all'uscita avverrà comunque la validazione.

O_PASSOK

Controlla se la validazione avviene ad ogni passaggio oppure solo se il campo è stato modificato. Normalmente l'ultima è vera. Settare O_PASSOK può essere utile se la vostra funzione di validazione può cambiare durante la elaborazione della form.

O_STATIC

Controlla se il campo è fissato alle sue dimensioni originali. Se spegnete questa opzione, il campo diventa dinamico e verrà allungato per contenere i dati inseritivi.

Una opzione di un campo non può venir cambiata quando il campo è correntemente selezionata. Comunque, le opzioni possono essere cambiate nei campi pubblicati che non siano correnti.

I valori delle opzioni sono maschere di bit e possono venir composti mediante OR logico nel modo noto.

5.5 Stato del Campo

Ogni campo ha un flag di stato che può essere posto a FALSE quando il campo è creato e a TRUE quando il valore nel buffer zero cambia. Questo flag può essere impostato e interrogato direttamente:

int set_field_status(FIELD *field,      /* campo da modificare */
                   int status);         /* modalità da impostare */

int field_status(FIELD *field);         /* cattura la modalità del campo */

Impostare questo campo da programma può essere utile se usate la stessa form ripetutamente, cercando ogni volta i campi modificati.

Chiamando field_status() su un campo non correntemente selezionato per l'input fornirà il valore corretto. Chiamando field_status() su un campo correntemente selezionato per l'input può non necessariamente dare il valore corretto, perchè i dati inseriti non vengono necessariamente copiati nel buffer zero prima del controllo di validazione in uscita.

Per garantire che il valore di stato ritornato rifletta la realtà, chiamate la field_status() sia (1) nella routine di validazione in uscita dal campo, che (2) dalle routine agganciate all'inizializzazione o terminazione, oppure (3) proprio dopo che la richiesta REQ_VALIDATION è stata elaborata dal driver della form.

5.6 Puntatore d'Utente del Campo

Ogni struttura di campo contiene un puntatore a carattere che non è usato dalla libreria form. Il suo uso è specifico all'applicazione per contenere dati privati del campo. È possibile manipolarlo mediante:

int set_field_userptr(FIELD *field,       /* campo da modificare */
                   char *userptr);        /* puntatore da inizializzare */

char *field_userptr(FIELD *field);        /* recupera il valore del puntatore */

(In verità questo puntatore dovrebbe essere di tipo (void *). Il tipo (char *) è mantenuto per compatibilità con System V.)

È permesso inizializzare il puntatore del campo di riferimento (con una chiamata a set_field_userptr() con il puntatore al campo posto a NULL.) Quando un nuovo campo viene creato, il puntatore d'utente del campo di riferimento viene copiato per inizializzare il puntatore d'utente del nuovo campo.

5.7 Campi a Dimensione Variabile

Normalmente, un campo è limitato alle dimensioni per esso specificate al momento della sua creazione. Se comunque viene disattivato il bit O_STATIC, il campo diventa dynamic e si ridimensionerà automaticamente per far posto ai dati che vengono inseriti. Se il campo ha buffer addizionali associati ad esso, questi cresceranno di pari passo con il buffer principale di input.

Un campo dinamico di una sola riga avrà altezza fissa (uno, appunto), ma larghezza variabile, scorrendo orizzontalmente i dati all'interno dell'area del campo originariamente dimensionata. Un campo dinamico a più righe avrà larghezza fissa, ma altezza variabile (numero di righe), scrollando verticalmente per visualizzare i dati all'interno dell'area del campo come originariamente dimensionata.

Normalmente ad un campo dinamico è consentito di crescere senza limite. Ma è possibile fissare un limite superiore alla dimensione di un campo dinamico. Lo fate con questa funzione:

int set_max_field(FIELD *field,     /* campo da modificare (non accetta NULL) */
                   int max_size);   /* limite superiore alla dimensione del campo */ 

Se il campo è a riga singola, max_size è il numero limite di colonne; se è a più righe, è il numero limite di righe. Per disabilitare ogni limite usate un argomento zero. Il limite di crescita può venir cambiato sia che il bit O_STATIC sia attivo o meno, ma non avrà effetto finchè non sarà attivo.

Le seguenti proprietà di un campo cambiano quando diventa dinamico:

5.8 Validazione del Campo

Inizialmente, ogni campo accetterà qualunque dato possa entrare nel suo buffer. Comunque è possibile attaccare un tipo validazione ad un campo. Se lo fate, ogni tentativo di lasciare un campo che contiene dati che non soddifano la validazione, fallirà. Alcuni tipi di validazione hanno anche un controllo di validità del carattere ad ogni carattere inserito nel campo.

Il controllo di validità di un campo (se esiste) non viene eseguito quando set_field_buffer() modifica il buffer di input, nè quando quel buffer viene modificato attraverso un campo linkato.

La libreria form dispone di un ricco insieme di tipi di validazione pre-definiti, e vi dà la capacità di definirne di vostri. Potete esaminare e cambiare gli attributi di validazione di un campo con le seguenti funzioni:

int set_field_type(FIELD *field,          /* campo da modificare */
                   FIELDTYPE *ftype,      /* tipo da associare */
                   ...);                  /* argomenti addizionali*/

FIELDTYPE *field_type(FIELD *field);      /* campo da interrogare */

Il tipo validazione di un campo è considerato un attributo del campo. Come per altri attributi di campo, eseguire set_field_type() su un campo NULL modifica l'attributo di validazione del campo di riferimento per i campi creati successivamente.

Ecco i tipi validazione pre-definiti:

TYPE_ALPHA

Questo tipo accessta dati alfabetici; niente spazi, cifre o caratteri speciali (questo controllo avviene all'input di ogni carattere). Viene impostato con:

int set_field_type(FIELD *field,          /* campo da modificare */
                   TYPE_ALPHA,            /* tipo da associare */
                   int width);            /* larghezza minima accettata */

L'argomento width imposta una quantità minima di dati. Tipicamente lo imposterete alla lunghezza del campo; se è più grande della larghezza del campo, il controllo di validità fallirà sempre. Una larghezza minima a zero rende l'inserimento nel campo opzionale.

TYPE_ALNUM

Questo tipo accetta dati alfanumerici; niente spazi, nè caratteri speciali (questo controllo avviene all'input di ogni carattere). Viene impostato con:

int set_field_type(FIELD *field,          /* campo da modificare */
                   TYPE_ALPHA,            /* tipo da associare */
                   int width);            /* larghezza minima accettata */

L'argomento width imposta una quantità minima di dati. Come nel TYPE_ALPHA, tipicamente lo imposterete alla lunghezza del campo; se è più grande della larghezza del campo, il controllo di validità fallirà sempre. Una larghezza minima a zero rende l'inserimento nel campo opzionale.

TYPE_ENUM

Questo tipo vi permette di restringere i valori di un campo ad uno specificato insieme di stringhe (per esempio, le sigle provinciali a due lettere). Viene impostato con:

int set_field_type(FIELD *field,          /* campo da modificare */
                   TYPE_ENUM,             /* tipo da associare */
                   char **valuelist;      /* lista di possibili valori */
                   int checkcase;         /* sensibilità alle maiuscole */
                   int checkunique);      /* deve essere univoco? */

Il parametro valuelist deve puntare ad una lista di stringhe valide terminata da NULL. L'argomento checkcase, se TRUE, rende il confronto sensibile alle maiuscole/minuscole.

Quando l'utente lascia un campo TYPE_ENUM, la procedura di validazione prova a completare i dati nel buffer con una stringa valida. Se una stringa interamente valida è stata inserita, naturalmente viene accettata. È inoltre possibile inserire la parte iniziale di una stringa valida ed ottenerla completa.

Inizialmente, se inserite un prefisso e questo coincide con più di un valore nella lista di stringhe, il prefisso verrà completato con lo primo valore coincidente. Ma se l'argomento checkunique è TRUE, si richiede che il prefisso coincida con un solo valore perchè venga accettato.

Le richieste REQ_NEXT_CHOICE e REQ_PREV_CHOICE possono essere particolarmente utili con questi campi.

TYPE_INTEGER

Questo tipo di campo accetta un intero. si imposta nel modo seguente:

int set_field_type(FIELD *field,          /* campo da modificare */
                   TYPE_INTEGER,          /* tipo da associare */
                   int padding,           /* numero di posti da riempire di zeri */
                   int vmin, int vmax);   /* valori limite */

Caratteri validi consistono in un ''meno'' iniziale opzionale e cifre. La verifica dei limiti viene eseguita all'uscita. Se il valore massimo è minore o uguale al minimo, i limiti vengono ignorati.

Se il valore supera il controllo sui limiti, vengono aggiunti tanti zeri iniziali quanti indicati nell'argomento padding.

Un valore in un buffer TYPE_INTEGER può venire convenientemente interpretato dalla funzione standard atoi(3).

TYPE_NUMERIC

Questo campo accetta un numero decimale. Viene impostato così:

int set_field_type(FIELD *field,          /* campo da modificare */
                   TYPE_INTEGER,          /* tipo da associare */
                   int padding,           /* precisione */
                   int vmin, int vmax);   /* valori limite */

Caratteri validi consistono in un ''meno''iniziale opzionale, cifre, un punto decimale. Il controllo sui limiti viene eseguito in uscita. Se il limita massimo è minore o uguale al limite minimo, i limiti vengono ignorati.

Se il valore supera il controllo sui limiti, vengono aggiunti tanti zeri iniziali quanti indicati nell'argomento padding.

Un valore in un buffer TYPE_NUMERIC può venire convenientemente interpretato dalla funzione standard atof(3).

TYPE_REGEXP

Questo tipo di campo accetta dati che soddisfino una espressione regolare. Viene impostato come segue:

int set_field_type(FIELD *field,          /* campo da modificare */
                   TYPE_REGEXP,           /* tipo da associare */
                   char *regexp);         /* espressione da confrontare */

La sintassi dell'espressione regolare è la stessa della regcomp(3). Il controllo sulla espressione regolare è eseguito in uscita.

5.9 Manipolazione diretta del Buffer del Campo

L'attributo più centrale di un campo è il contenuto del suo buffer. Quando una form è stata completata, la vostra applicazione di solito ha bisogno di conoscere lo stato del buffer di ogni campo. Potete trovarlo con questa:

char *field_buffer(FIELD *field,          /* campo da interrogare */
                   int bufindex);         /* numero del buffer da interrogare */

Di norma, lo stato del buffer numero zero di ciascun campo è impostato dalle azioni di editazione dell'utente su quel campo. È alle volte utile poter impostare il valore del buffer numero zero (o altri) dalla vostra applicazione:

int set_field_buffer(FIELD *field,        /* campo da modificare */
                   int bufindex,          /* numero del buffer da modificare */
                   char *value);          /* valore stringaa cui impostare il buffer */

Se il campo non è abbastanza largo e non può essere ridimensionato ad una dimensione sufficiente per contenere il valore specificato, il valore verrà troncato alla misura.

Chiamare la field_buffer() con un puntatore NULL genera un errore. Chiamare la field_buffer() su un campo non correntemente selezionato produce un valore corretto. Chiamare la field_buffer() su un campo correntemente selezionato per l'input può non fornire necessariamente il corretto valore del buffer, perchè i dati inseriti non vengono necessariamente copiati nel buffer zero prima della validazione in uscita.

Per garantire che il valore del buffer ritornato rifletta la realtà sullo schermo, chiamate la field_buffer() sia (1) nella routine di validazione in uscita dal campo, che (2) dalle routine agganciate all'inizializzazione o terminazione del campo o della form, oppure (3) proprio dopo che la richiesta REQ_VALIDATION è stata elaborata dal driver della form.

5.10 Attributi delle Form

Come con gli attributi di campo, gli attributi di form ereditano valori da una struttura form di riferimento. Questi valori iniziali possono essere interrogati o modificati da una di queste funzioni usando NULL come argomento del parametro puntatore a form.

Il più importante attributo di una form è la sua lista di campi. Potete interrogare e cambiare questa lista con:

int set_form_fields(FORM *form,           /* form da modificare */
                    FIELD **fields);      /* campi da connettere */
char *form_fields(FORM *form);            /* recupera i campi della form */
int field_count(FORM *form);              /* conta i campi connessi */

Il secondo argomento della set_form_fields() può essere un vettore terminato da NULL di puntatori a campi come quello richiesto dalla new_form(). In questo caso i vecchi campi della form sono disconnessi ma non rilasciati (e quindi in grado di essere connessi ad altre form), quindi i nuovi campi vengono connessi.

Può anche essere NULL, nel qual caso i vecchi campi sono disconnessi (e non rilasciati) ma nessun nuovo campo viene connesso.

La funzione field_count() semplicemente ritorna il numero di campi connessi ad una data form. Ritorna -1 se l'argomento puntatore a form è NULL.

5.11 Controllo sulla Visualizzazione della Form

Nella sezione iniziale, dicemmo che per visualizzare una form normalmente si parte definendo le sue dimensioni (e i campi), pubblicandola, e rinfrescando lo schermo. C'è un passo nascosto prima della pubblicazione, che consiste nell'associazione della form con una finestra riquadro (attualmente una coppia di finestre) in cui verrà visualizzata. Inizialmente, la libreria form associa ogni form con la finestra tutto-schermo stdscr.

eseguendo esplicitamente questo passo intermedio, potrete associare una form con una finestra riquadro dichiarata sul vostro schermo. Questo può essere utile se volete adattare la form a diverse dimensioni dello schermo, legare dinamicamente le form allo schermo o usare una form in una interfaccia gestita da pannelli.

Le due finestre associate con ciascuna form hanno le stesse funzioni delle loro analoghe nella libreria menù. Entrambe queste finestre sono visualizzate quando la form è pubblicata, e cancellate quando è spubblicata.

La finestra più esterna non è in alcun modo toccata dalle routine della form. Esiste affinchè il programmatore possa associarvi un titolo, un bordo o perfino testo di help insieme con la form ed averlo correttamente rinfrescato o cancellato al momento giusto. La finestra interna o sottofinestra è dove la form viene attualmente visualizzata.

Allo scopo di dichiarare la vostra finestra riquadro per una form, avete bisogno di sapere le dimensioni del rettangolo che contiene la form. Potete ottenere questa informazione con:

int scale_form(FORM *form,                /* form da consultare */
               int *rows,                 /* righe della form */
               int *cols);                /* colonne della form */

Le dimensioni della form sono inserite nelle locazioni puntate dagli argomenti. Una volta che avete queste informazioni, potete usarle per dichiarare le finestre, quindi usare una di queste funzioni:

int set_form_win(FORM *form,              /* form da modificare */
                 WINDOW *win);            /* finestra da connettere */

WINDOW *form_win(FORM *form);             /* ritorna la finestra della form */

int set_form_sub(FORM *form,              /* form da modificare */
                 WINDOW *win);            /* sottofinestra da connettere */

WINDOW *form_sub(FORM *form);             /* ritorna la sottofinestra della form */

Notate che le operazioni curses, inclusa la wrefresh(), sulla form, vanno eseguite sulla finestra, non sulla sottofinestra.

È possibile controllare dalla vostra applicazione se tutto un campo scrollabile è visualizzato attualmente nella sottofinestra. Usate queste funzioni:

int data_ahead(FORM *form);               /* form da interrogare */ 

int data_behind(FORM *form);              /* form da interrogare */ 

La funzione data_ahead() ritorna TRUE se (a) il campo corrente è a singola riga ed ha dati non-visualizzati alla destra, (b) il campo corrente è multi-riga e ci sono dati fuori schermo di sotto.

La funzione data_behind() ritorn TRUE se il carattere alla prima posizione (a sinistra in alto) è fuori schermo (non visualizzato).

Infine, c'è una funzione per ripristinare il cursore della finestra della form al valore atteso dal driver delle form:

int pos_form_cursor(FORM *)               /* form da interrogare */

Se la vostra applicazione modifica il cursore della finestra della form, chiamate questa funzione prima di restituire il controllo al driver delle form allo scopo di re-sincronizzarlo.

5.12 Gestione dell'Input nel Driver delle Form

La funzione form_driver() gestisce richieste di input virtuali per la navigazione, editazione, validazione della form, proprio come la menu_driver fa per i menù (vedere la sezione Gestione dell'Input del menù ).

int form_driver(FORM *form,               /* form a cui passare l'input */
                int request);             /* codice richiesta */

La vostra funzione di input virtuale deve prendere l'input e convertirlo o in un carattere alfanumerico (che viene trattato come dato da inserire nel campo correntemente selezionato), o una richiesta di elaborazione della form.

Il driver delle form fornisce agganci (attraverso le funzioni di validazione dell'input e di terminazione del campo) con cui la vostra applicazione può controllare che l'input ricevuto soddisfi quello atteso.

Richieste di Navigazione di Pagina

Queste richieste causano movimenti a livello di pagina nella form, facendo scattare la visualizzazione di una nuova schermata.

REQ_NEXT_PAGE

Si sposta alla prossima pagina nella form.

REQ_PREV_PAGE

Si sposta alla precedente pagina nella form.

REQ_FIRST_PAGE

Si sposta alla prima pagina della form.

REQ_LAST_PAGE

Si sposta all'ultima pagina della form.

Queste richieste usano la lista come ciclica; cioè, REQ_NEXT_PAGE dall'ultima pagina riporta alla prima, e REQ_PREV_PAGE dalla prima pagina porta all'ultima.

Richieste di Navigazione tra Campi

Queste richieste gestiscono la navigazione tra campi di una stessa pagina.

REQ_NEXT_FIELD

Si sposta al prossimo campo.

REQ_PREV_FIELD

Si sposta al campo precedente.

REQ_FIRST_FIELD

Si sposta al primo campo.

REQ_LAST_FIELD

Si sposta all'ultimo campo.

REQ_SNEXT_FIELD

Si sposta al prossimo campo sullo schermo.

REQ_SPREV_FIELD

Si sposta al precedente campo sullo schermo.

REQ_SFIRST_FIELD

Si sposta al primo campo sullo schermo.

REQ_SLAST_FIELD

Si sposta all'ultimo campo sullo schermo.

REQ_LEFT_FIELD

Si sposta sul campo alla sinistra.

REQ_RIGHT_FIELD

Si sposta sul campo alla destra.

REQ_UP_FIELD

Si sposta al campo di sopra.

REQ_DOWN_FIELD

Si sposta al campo di sotto.

Queste richieste considerano la lista di campi in una pagina come ciclica; cioè REQ_NEXT_FIELD dall'ultimo riporta al primo, e REQ_PREV_FIELD dal primo riporta all'ultimo. L'ordine dei campi per queste (più le REQ_FIRST_FIELD e REQ_LAST_FIELD) segue l'ordine dei puntatori nel vettore della form (come costruito dalla new_form() o dalla set_form_fields().

È inoltre possibile scorrere i campi come se fossero stati ordinati in base alla loro posizione sullo schermo, in una sequenza che va da sinistra a destra e dall'alto verso il basso. Per far ciò, utilizzate il secondo gruppo di richieste.

Infine, è possibile muoversi tra i campi usando le direzioni su, giù, destra e sinistra. Per ottenere questo, usate il terzo gruppo di richieste. Notate, comunque, che la posizione di un campo, per lo scopo di queste richieste, è quella del suo angolo superiore sinistro.

Per esempio, supponete di avere il campo multi-riga B, e due campi a singola riga A e C sulla stessa riga di B, con A alla sinistra di B e C alla destra di B. Una REQ_MOVE_RIGHT da A porterà a B solo se A, B e C hanno tutti la prima riga in comune; altrimenti salterà B verso C.

Richieste di Navigazione nel Campo

Queste richieste guidano i movimenti del cursore all'interno del campo correntemente selezionato.

REQ_NEXT_CHAR

Muove al prossimo carattere.

REQ_PREV_CHAR

Muove al precedente carattere.

REQ_NEXT_LINE

Muove alla prossima riga.

REQ_PREV_LINE

Muove alla precedente riga.

REQ_NEXT_WORD

Muove alla prossima parola.

REQ_PREV_WORD

Muove alla precedente parola.

REQ_BEG_FIELD

Muove all'inizio del campo.

REQ_END_FIELD

Muove alla fine del campo.

REQ_BEG_LINE

Muove all'inizio della riga.

REQ_END_LINE

Muove alla fine della riga.

REQ_LEFT_CHAR

Muove alla sinistra nel campo.

REQ_RIGHT_CHAR

Muove alla destra nel campo.

REQ_UP_CHAR

Muove sù nel campo.

REQ_DOWN_CHAR

Muove giù nel campo.

Ogni parola è separata dai precedenti e dai successivi caratteri tramite spazi. I comandi per muovere all'inizio e alla fine della riga o del campo, cercano il primo o l'ultimo carattere non riempitivo.

Richieste di Scorrimento

Campi dinamici che siano cresciuti e campi esplicitamente creati con righe fuori schermo sono scorrevoli. Campi a singola riga scorrono orizzontalmente; campi multi-riga scorrono verticalmente. La maggior parte dello scorrimento è attivata dall'editing e da movimenti nel campo (la libreria fa scorrere il campo per mantenere visibile il cursore). È possibile ottenere esplicitamente lo scorrimento con le seguenti richieste:

REQ_SCR_FLINE

Scorre verticalmente in avanti di una riga.

REQ_SCR_BLINE

Scorre verticalmente indietro di una riga.

REQ_SCR_FPAGE

Scorre verticalmente in avanti di una pagina.

REQ_SCR_BPAGE

Scorre verticalmente indietro di una pagina.

REQ_SCR_FHPAGE

Scorre verticalmente in avanti di mezza pagina.

REQ_SCR_BHPAGE

Scorre verticalmente indietro di mezza pagina.

REQ_SCR_FCHAR

Scorre orizzontalmente in avanti di un carattere.

REQ_SCR_BCHAR

Scorre orizzontalmente indietro di un carattere.

REQ_SCR_HFLINE

Scorre orizzontalmente in avanti della lunghezza del campo.

REQ_SCR_HBLINE

Scorre orizzontalmente indietro della lunghezza del campo.

REQ_SCR_HFHALF

Scorre orizzontalmente in avanti di metà lunghezza del campo.

REQ_SCR_HBHALF

Scorre orizzontalmente indietro di metà lunghezza del campo.

Negli scorrimenti, una pagina di un campo e l'altezza della sua parte visibile.

Richieste di Editazione

Il passare un carattere ASCII al driver delle form viene trattato come fosse una richiesta di inserire il carattere nel buffer del campo. Dipende dalla modalità di edit se questo è un inserimento oppure una ricopertura (inizialmente è inserimento).

Le seguenti richieste supportano l'editazione del campo ed il cambio della modalità di edit:

REQ_INS_MODE

Imposta la modalità inserimento.

REQ_OVL_MODE

Imposta la modalità riscrittura.

REQ_NEW_LINE

Richiesta di a-capo (vedi sotto per chiarimenti).

REQ_INS_CHAR

Inserisce uno spazio nella locazione corrente.

REQ_INS_LINE

Inserisce una riga vuota alla locazione corrente.

REQ_DEL_CHAR

Cancella il carattere sotto il cursore.

REQ_DEL_PREV

Cancella la parola che precede il cursore.

REQ_DEL_LINE

Cancella la riga dove si trova il cursore.

REQ_DEL_WORD

Cancella la parola su cui è il cursore.

REQ_CLR_EOL

Cancella fino alla fine della riga.

REQ_CLR_EOF

Cancella fino alla fine del campo.

REQ_CLEAR_FIELD

Cancella l'intero campo.

Il comportamento della richiesta REQ_NEW_LINE e REQ_DEL_PREV è complicato e in parte controllato da un paio di opzioni della form. Casi speciali si attivano quando il cursore si trova all'inizio di un campo, o sull'ultima riga di un campo.

Prima consideriamo la REQ_NEW_LINE:

Il normale comportamento della REQ_NEW_LINE in modalità inserimento è di spezzare la riga corrente alla posizione del cursore, inserendo la parte della riga che si trova a destra del cursore come una nuova riga sotto la riga corrente e spostando il cursore all'inizio di questa nuova linea (si può pensare a questo come all'inserimento del carattere di newline nel buffer del campo).

Il normale comportamento della REQ_NEW_LINE nella modalità di riscrittura è di pulire la riga corrente dalla posizione del cursore fino alla fine della riga. Il cursore viene quindi spostato all'inizio della nuova linea.

Comunque, la REQ_NEW_LINE all'inizio di un campo o sull'ultima riga di un campo esegue invece la REQ_NEXT_FIELD. Se l'opzione O_NL_OVERLOAD è disabilitata, allora anche questa speciale azione è disabilitata.

Ora prendiamo in considerazione la REQ_DEL_PREV:

Il normale comportamento della REQ_DEL_PREV è di cancellare il carattere precedente. Se la modalità di inserimento è attiva, ed il cursore si trova all'inizio di una riga e il testo di quella riga può essere contenuto nella riga precedente, piuttosto unisce il contenuto della riga corrente con la riga precedente e pulisce la riga corrente (potete pensare a questo come alla cancellazione del carattere di newline dal buffer del campo).

Comunque, la REQ_DEL_PREV all'inizio del campo esegue invece la REQ_PREV_FIELD. Se l'opzione O_BS_OVERLOAD è disabilitata, allora anche questa speciale azione è disabilitata e il driver dell form ritorna E_REQUEST_DENIED.

Vedere Opzioni della Form per una discussione su come impostare ed annullare le opzioni di sovrascrittura.

Richieste di Ordinamento

Se il campo è di tipo lista ordinata, ed ha funzioni associate per prelevare il successivo ed il precedente valore dalla lista data, queste sono le richieste che possono fornire il valore nel buffer:

REQ_NEXT_CHOICE

Prende il valore successivo dalla lista e lo pone nel buffer.

REQ_PREV_CHOICE

Prende il valore precedente dalla lista e lo pone nel buffer.

Tra i tipi di campo pre-definiti solo TYPE_ENUM è fornito di funzioni pre-definite di recupero precedente e succssivo. Quando definite un vostro proprio tipo di campo (see Tipi di Validazione Aggiunti ), potrete associarvi vostre funzioni di ricerca in sequenza.

Comandi specifici all'Applicazione

Le richieste specifiche alla form sono rappresentate da interi al di sopra del valore curses più grande di KEY_MAX e minore o uguale a MAX_COMMAND. Se la vostra routine di virtualizzazione dell'input ritorna un valore superiore a MAX_COMMAND, il driver delle form lo ignorerà.

5.13 Funzioni Agganciate ai Cambiamenti nel Campo

È possibile impostare agganci a funzioni da eseguire ogniqualvolta il campo corrente o la form cambino. Ecco le funzioni che supportano questo:

typedef void    (*HOOK)();       /* puntatore a funzione che ritorna void */

int set_form_init(FORM *form,    /* form da modificare */
                  HOOK hook);    /* aggancio all'inizializzazione */

HOOK form_init(FORM *form);      /* form da interrogare */

int set_form_term(FORM *form,    /* form da modificare */
                  HOOK hook);    /* aggancio alla terminazione */

HOOK form_term(FORM *form);      /* form da interrogare */

int set_field_init(FORM *form,   /* form da modificare */
                  HOOK hook);    /* aggancio all'inizializzazione */

HOOK field_init(FORM *form);     /* form da interrogare */

int set_field_term(FORM *form,   /* form da modificare */
                  HOOK hook);    /* aggancio alla terminazione */

HOOK field_term(FORM *form);     /* form da interrogare */

Queste funzioni vi permettono di impostare o verificare quattro diversi agganci. In ciascuna delle funzioni che impostano, il secondo argomento deve essere l'indirizzo di una funzione d'aggancio. Queste funzioni differiscono solo per il momento in cui vengono eseguite.

form_init

Questo aggancio viene chiamato quando la form viene pubblicata; ed anche ad ogni operazione di cambio pagina.

field_init

Questo aggancio viene chiamato quando la form viene pubblicata; ed anche ad ogni cambio di campo.

field_term

Questo aggancio viene chiamato proprio dopo la validazione del campo; cioè proprio prima che il campo venga modificato. Viene inoltre chiamato quando la form viene spubblicata.

form_term

Questo aggancio viene chiamato quando la form viene spubblicata; ed anche ad ogni operazione di cambio pagina.

La chiamata di questi agganci può essere attivata

  1. Quando richieste di editazione vengono esaminate dal driver delle form
  2. Quando la pagina corrente viene cambiata dalla set_current_field()
  3. Quando il campo corrente viene cambiato dalla set_form_page()

Vedere Comandi di Cambiamento di Campo per una discussione sugli ultimi due casi.

Potete inizialmente impostare un aggancio per tutti i campi chiamando una di queste funzioni con il primo argomento a NULL.

Per disabilitare ogniuno di questi agganci basta reimpostarlo al valore iniziale, NULL.

5.14 Comandi di Cambiamento di Campo

Normalmente, la navigazione attraverso la form è guidata dall'utente con richieste di input. Ma alcune volte e utile poter cambiare il punto di editazione e di vista dall'interno della vostra applicazione, o domandare che campo è correntemente attivo. Le seguenti funzioni vi aiutano ad ottenere questo:

int set_current_field(FORM *form,         /* form da modificare */
                      FIELD *field);      /* campo su cui spostarsi */

FIELD *current_field(FORM *form);         /* form da interrogare */

int field_index(FORM *form,               /* form da interrogare */
                FIELD *field);            /* campo di cui si vuole sapere l'indice */

La funzione field_index() ritorna l'indice di un dato campo nel vettore di campi della form data (il vettore passato alla new_form() o alla set_form_fields()).

Il campo corrente iniziale è il primo campo attivo della prima pagina. La funzioneset_current_field() lo reimposta.

È anche possibile nuoversi tra le pagine.

int set_form_page(FORM *form,             /* form da modificare */
                  int page);              /* pagina a cui andare (0=inizio) */

int form_page(FORM *form);                /* ritorna la pagina corrente nella form */

La pagina iniziale di una form appena creata è la 0. La funzione set_form_page() la modifica.

5.15 Opzioni della Form

Come i campi, le form hanno dei bit opzione di controllo, che possono essere cambiati o ispezionati con queste funzioni:

int set_form_opts(FORM *form,             /* form da modificare */
                  int attr);              /* attributo da impostare */ 

int form_opts_on(FORM *form,              /* form da modificare */
                 int attr);               /* attributi da attivare */ 

int form_opts_off(FORM *form,             /* form da modificare */
                  int attr);              /* attributi da disattivare */ 

int form_opts(FORM *form);                /* form da interrogare */

Inizialmente tutte le opzioni sono attiva. Ecco le opzioni disponibili:

O_NL_OVERLOAD

Abilita lo speciale comportamento della REQ_NEW_LINE come descritto in Richieste di Editazione . Il valore di questa opzione è ignorato nei campi dinamici che non hanno raggiunto la loro dimensione limite; questi non hanno un'ultima linea, così la circostanza per attivare la REQ_NEXT_FIELD non giunge mai.

O_BS_OVERLOAD

Abilita lo speciale comportamento della REQ_DEL_PREV come descritto in Richieste di Editazione .

I valori delle opzioni sono maschere di bit e possono venir composti mediante OR logico nel modo noto.

5.16 Tipi di Validazione Aggiunti

La libreria form vi dà la possibilità di definire da voi dei tipi di validazione aggiuntivi. In più, gli argomenti opzionali aggiuntivi della set_field_type vi permettono effettivamente di parametrizzare i tipi di validazione. La maggior parte delle complicazioni nell'interfaccia dei tipi di validazione ha a che fare con la gestione degli argomenti addizionali nelle funzioni di validazione aggiunte.

Unione di Tipi

Il modo più semplice per creare un tipo di dato aggiuntivo è di comporlo unendone due già esistenti:

FIELD *link_fieldtype(FIELDTYPE *type1, 
                      FIELDTYPE *type2);

Questa funzione crea un tipo di campo che accetterà ogni valore valido in uno dei due tipi di campo in argomento (che possono essere sia pre-esistenti o definiti da programma).

Se una chiamata a set_field_type() successivamente richiede argomenti, il numvo tipo composito si aspetta tutti gli argomenti per il primo tipo, quindi tutti gli argomenti per il secondo. Funzioni di ordinamento (vedere Richieste di Ordinamento ) in associazione con i tipi compositi lavorano in modo composito: ciò che fanno è di controllare la funzione di validazione per il primo tipo, poi per il secondo, per immaginarsi in base a quale tipo i contenuti del buffer andrebbero elaborati.

Nuovi Tipi di Campo

Per creare dal nulla un tipo di campo, dovete costruire una o entrambe le seguenti:

Ecco come potete farlo:

typedef int     (*HOOK)();       /* puntatore a funzione che ritorna un int */

FIELDTYPE *new_fieldtype(HOOK f_validate, /* validatore di campo */
                         HOOK c_validate) /* validatore di carattere */


int free_fieldtype(FIELDTYPE *ftype);     /* tipo da rilasciare */

Almeno uno dei due argomenti della new_fieldtype() deve essere non NULL. Il driver delle form chiamerà automaticamente le funzioni di validazione del nuovo tipo dai punti giusti nell'elaborazione di un campo del nuovo tipo.

La funzione free_fieldtype() de-alloca il tipo di campo in argomento, rilasciando tutta la memoria ad esso associata.

Di norma, un validatore di campo viene chiamato quando l'utente cerca di lasciare il campo. Il suo primo argomento è un puntatore a campo, da cui ottenere il buffer zero e controllarlo. Se la funzione ritorna TRUE, l'operazione ha successo; se ritorna FALSE, il cursore di editazione rimane fermo sul campo.

Un validatore di caratteri preleva il carattere dal suo primo argomento. Dovrebbe ritornare TRUE se il carattere è accettabile, altrimenti FALSE.

Argomenti delle Funzioni di Validazione

Alle vostre funzioni di validazione di campo e di carattere viene comunque passato un secondo argomento. Si tratta dell'indirizzo di una struttura (che chiameremo mucchio) costruita con ciascuno degli argomenti specifici al tipo di campo passati alla set_field_type(). Se non ci sono di questi argomenti definiti per il tipo di campo, il puntatore al mucchio sarà NULL.

Allo scopo di far sì che tali argomenti vengano passati alle vostre funzioni di validazione, dovrete associare al tipo un piccolo insieme di funzioni di gestione della memoria. Il driver delle form le utilizzerà per accumulare un mucchio dagli argomenti agganciati a ciascun argomento della set_field_type(), e un puntatore al mucchio verrà passato alle funzioni di validazione.

Ecco come creare l'associazione:

typedef char    *(*PTRHOOK)();    /* puntatore a una funzione che ritorna (char *) */
typedef void    (*VOIDHOOK)();    /* puntatore a una funzione che ritorna void */

int set_fieldtype_arg(FIELDTYPE *type,    /* tipo da modificare */
                      PTRHOOK make_str,   /* crea la struttura dagli argomenti */
                      PTRHOOK copy_str,   /* crea una copia della struttura */
                      VOIDHOOK free_str); /* rilascia la memoria della struttura */

Ecco come vengono usate le funzioni aggancio per la gestione della memoria:

make_str

Questa funzione viene chiamata da set_field_type(). Vuole un argomento, una lista variabile (va_list) degli argomenti specifici al tipo, passati alla set_field_type(). Ci si aspetta che ritorni un puntatore ad un mucchio, cioè alla struttura di dati che incapsula questi argomenti.

copy_str

Questa funzione viene chiamata dalle funzioni della libreria form che allocano nuove istanze del campo. Prende un puntatore ad un mucchio, copia il mucchio nella memoria allocata e ritorna l'indirizzo della copia.

free_str

Questa funzione viene chiamata dalle routine di de-allocazione dei campi e dei tipi contenute nella libreria. Vuole un argomento puntatore a mucchio, e ci si aspetta che ne rilasci la memoria.

Le funzioni make_str e copy_str possono ritornare NULL per indicare fallimenti nell'allocazione di memoria. Le routine di libreria che le chiamano ritorneranno un'indicazione di errore quando questo accade. Perciò le vostre funzioni di validazione non riceveranno mai un NULL in un puntatore a mucchio, e quindi non hanno bisogno di gestirlo.

Funzioni di Ordinamento per Tipi Aggiunti

Alcuni tipi di campo aggiunti sono ordinati semplicamente nello stesso e ben definito modo che è TYPE_ENUM. Per tali tipi, è possibile definire funzioni successore e predecessore per supportare le richieste REQ_NEXT_CHOICE e REQ_PREV_CHOICE. Ecco come:

typedef int     (*INTHOOK)();     /* pointer to function returning int */

int set_fieldtype_arg(FIELDTYPE *type,    /* tipo da modificare */
                      INTHOOK succ,       /* funzione che dà il successore */
                      INTHOOK pred);      /* funzione che dà il precessore */

Le funzioni argomento precessore e successore riceveranno ciascuna due argomenti; un puntatore a campo e un puntatore a mucchio (come le funzioni di validazione). Ci si aspetta che queste usino la funzione field_buffer() per leggere il valore corrente, e la set_field_buffer() sul buffer zero per impostare il precedente o successivo valore. Ogni funzione aggancio può ritornare TRUE per indicare successo (l'impostazione di un valido valore precedente o successivo) oppure FALSE per indicare fallimento.

Come Evitare Problemi

L'interfaccia per definire tipi aggiunti è complicata e rischiosa. Piuttosto che buttarvi a creare un tipo aggiunto da zero, dovreste incominciare studiandovi il codice sorgente della libreria per qualunque siano i tipi predefiniti che sembrino più vicini ai vostri bisogni.

Usate quel codice come modello ed evolvetelo verso ciò che volete realmente. In questo modo vi eviterete parecchi problemi e perdite di tempo. Il codice sorgente della libreria ncurses non è coperto da copyright proprio per permettere questo uso.

Se il vostro tipo aggiunto definisce funzioni di ordinamento, cercate di trattare in modo intuitivo il campo vuoto. Una convenzione utile dice che il successore di un campo vuoto è il valore più piccolo, ed il suo predecessore quello più grande.


Precedente Successivo Indice