1   Introduzione

Questo tutorial presuppone conoscenze di base su yab, C, C++ e probabilmente sulle API di BeOS (BeAPI) per aggiungere nuovi comandi a yab. Non sono necessarie conoscenze di flex e bison.

2   Progettare un Comando

Un tipico comando yab o è una procedure (una funzione che ritorna void), una funzione che ritorna un numero (double o int) o una funzione che ritorna una stringa (char*).

Un comando è formato da

   -   le parole del comando (entità lessicali)

   -   la regola del comando

   -   la funzione di wrapping C che chiama il metodo C++

   -   il metodo C++

Ciò sarà discusso dettagliatamente qui di seguito.

2.1   Le parole del comando

Le parole necessarie ad un comando (tokens) sono definite nel file yabasic.flex. Per favore introdurre nuove parole del comando, solo quando quelle esistenti non sono sufficienti a descrivere i vostri nuovi comandi. Navigate nel file per trovare tutti i comandi yab e yabasic.

Una parola del comando è formata dalla parola stessa (in maiuscolo) e dal token che ritorna. Il token molto spesso è semplicemente la parola stessa con una t davanti. Ad esempio:

Example:

BUTTON return tBUTTON;
Note: Ci sono eccezioni per il nome del token, es. tGet rappresenta la parola di comando tGet mentre tGETNUM rappresenta GET.

I nuovi toke vanno dichiarati anche nel file yabasic.bison, semplicemente aggiungendo il nome del token nella lista all'inizio del file.

2.2   La regola del comando

L'attuale regola del comando (grammatica) deve essere aggiunta al file yabasic.bison. Se scorrete il file avrete una buona idea di come deve apparire la grammatica.

Fondamentalmente in questo file sono interessanti tre sezioni: la sezione per le procedure, la sezione per le funzioni numeriche e la sezione per le funzioni che ritornano una stringa. Indagheremo le differenze tra queste funzioni nelle prossime due sezioni. Ma prima diamo un'occhiata alle similitudini. Una regola del comando è scritta con un leading pipe | seguito dai token e dagli argomentif. Alla fine segue una funzione identifier.

Esempio:

| tBUTTON coordinates to coordinates ',' string_expression ',' string_expression ',' string_expression 
{add_command(cBUTTON,NULL);}

Qui, coordinates è un sostituto per expression ',' expression e to è un sostituto del comando TO (che comunque è solo ',').

Così fondamentalmente ci sono due tipi di argomenti: expression è un numero (di tipo double) e string expression è una stringa (di tipo char*). Questi possono essere separati da parentesi ','.

Sono possibili anche ('(' and ')') ma non li ho utilizzati spesso. Così in questo esempio il comando BUTTON ha 7 argomenti, 4 numeri per le coordinate e 3 stringhe che conterranno il suo ID, il testo del bottone e la ID della view.

2.3   Procedure

Come in tutte le procedure, l'esempio menzionato no ritorna un valore (in C è una funzione void). Che da un identificatore interno chiamato cBUTTON.

Questo identificatore va dichiarato nel file yabasic.h. Date un'occhiata alla lista degli altri identificatori. Inoltre, in yabasic.h va dichiarato il nome della funzione C che è stata aggiunta al file graphic.c . Le procedure danno sempre una struct di informazioni circa argomenti, linee number ecc. Cioè, in graphic.c la funzione C semplicemente trasmetterà queste informazioni alla classe principale C++ (*, YabInterface *).

Prima di aggiungere la funzione in grephic.c essa va aggiunta anche in main.c . La, aggiungetela allo switch che chiama le funzioni in accordo al loro identificatore.

Esempio:

case cBUTTON 
           createbutton(current, yab); DONE;

Una tipica funzione void in graphic.c appare come nel seguente esempio:

void ceatebuttin(struct command *cmd, YabInterface* yab) 
{ 
      double x1,y1,x2,y2; 
      char *id, *title, *view; 

      view = pop(stSTRING)->pointer; 
      title = pop(stSTRING)->pointer; 
      id = pop(stSTRING)->pointer; 
      y2=pop(stNUMBER)->value; 
      x2=pop(stNUMBER)->value 
      y1=pop(stNUMBER)->value; 
      x1=pop(stNUMBER)->value; 

      yi_SetCurrentLineNumber(cmd->line, (const char*)cmd->lib->s, yab); 
      yi_CreateButton(x1,y1,x2,y2, id, title, view, yab); 
}

Nel nostro esempio, prima i 7 argomenti yab vengono recuperati dalla struct struct comando.

Nota: gli argomenti sono salvati sullo stack, per cui dovete recuperarli in ordine inverso! Qui, ad es. y1 viene recuperato prima di x1. Anche, le stringhe e i numeri hanno differenti pop calls.

I numeri possono essere double o int ma per le coordinate, vanno utilizzati sempre i double.

La corrente linea di numeri viene passata alla classe YabInterface class chiamando yi_SetCurrentLineNumber. Questa linea è la stessa per tutte le funzioni void.

Finalmente, gli argomenti vengono passati alla classe YabInterface class chiamando yi_CreateButton.

2.4   Funzioni

Le funzioni che restituiscono un numero o una stringa sono implementate differentemente. Per prima cosa hanno un differente identificatore iniziale con una f es. fLISTBOXGETNUM. Questo identificatore va dichiarato in yabasic.h nella funzione enum.

Nota: le funzioni vengono ordinate in base al numero dei loro argomenti!

Inoltre il nome della funzione C che è stata aggiunta al file graphic.c va dichiarato anche in yabasic.h. Differentemente che per le procedure, queste funzioni impostano immediatamente il loro argumento, es.:

int listboxgetnum(const char*, YabInterface *yab, int line, const char* libname);

listboxgetnum ritorna un int quando gli viene passata una stringa come primo argomento. Gli argomenti che seguono YabInterface *yab, int line, const char* libname vanno aggiunti per fornire alla classe YabInterface ulteriori informazioni.

Oltre alla procedura, il recupero degli argomenti dallo stack e la chiamata alla funzione sono tutti effettuati nel file function.c. Li gli argomenti yab vengono recuperati dallo stack ed inoltrati alla funzione wrapper in graphic.c.

Esempio:

case fLISTBOXGETNUM: 
        str=a1->pointer; 
        value = listboxgetnum(str, yab, linenum, current->lib->s); 
        result = stNUMBER; 
        break;

L'argomento stringa è recuperato da a1->pointer. Gli argomenti numerici vengono indirizzati da es. a3->value (non presente in questo esempio). Gli argomenti vengono numerati da a1 ad a6. Al momento ulteriori argomenti non sono supportati.

Qui, il risultato è archiviato come un numero. Per le stringhe, il risultato potrebbe essere archiviato in un pointer and result = stSTRING; va impostato. Date un'occhiata agli altri comandi in questo file per ulteriori esempi.

Finalmente, la funzione wrapper va implementate in graphic.c. Per il nostro esempio, ciò appare come nel seguente codice:

int listboxgetnum(const char* id, YabInterface *yab, int line, const char* libname) 
{ 
        yi_SetCurrentLineNumber(line, libname, yab); 
        return yi_ListboxGetNum(id, yab); 
}

La line number corrente viene passata alla classe YabInterface chiamando yi_SetCurrentLineNumber. Questa linea è la stessa per tutte le funzioni. Il numero tornato viene semplicemente passato da yi_ListboxgetNum.

Nota: le stringhe vanno copiate con my_strdup, es. return my_strdup((char*)yi_CheckMessages(yab)); Spetta a voi fare in modo che le stringhe siano ancora in memoria quando sono state copiate!

3    La classe C++ YabInterface

3.1   Aggiungere un Metodo

Dopo quanto descritto sopra, siamo pronti a scrivere un nuovo metodo. Il metodo ha un nome C++ e una funzione wrapper con un nome esterno che inizia con yi . Entrambe vanno definite in YabInterface.h e implementate in YabInterface.cpp.

La funzione wrapper passa il puntatore all'oggetto <>YabInterface e chiama il metodo principale:

void yi_CreateButton(double x1, double y1, double x2, double y2, const char* id, const char* title, const char* view, YabInterface* yab) 
{ 
     yab->CreateButton(BRect(x1,y1,x2,y2), id, _L/title), view), 
}
Notate la macro _L() utilizzata sul testo che va tradotto automaticamente dal local kit di Zeta.

Il metodo stesso è una parte della classe YabInterface che è derivata da BApplication. Così ogni metodo BApplication è accessibile dal vostro metodo.

void YabInterface::CreateButton(BRect frame, const char* id, const char* title, const char* view) 
{ 
     // code here 
}



3.2   Accesso alla strutture dati yab

yab archivia varie informazioni nelle liste degli oggetti. La lista più desiderata è la lista delle view disponibili. Queste sono archiviate nel YabList object viewList. Per inizializzare un nuovo widget, è spesso sufficiente trovare la parent view. Ciò viene fatto chiamando YabList::GetView(const char*):

YabView *myView = cast_as((BView*)viewList->GetView(view), YabView); 
if(myView) 
{ 
     YabWindow *w = cast_as(myView->Window(), YabWindow); 
if(w) 
       { 
         w->Lock(); 
         // inizialize widget here 
         w->unlock(); 
       } 
       else          ErrorGen("Unalbe to lock window"); } 
else 
     Error(view, "VIEW");

Nuovi widgets potrebbero permetter alcuni utili layout. Per favore fate riferimento ai comandi BUTTON e LISTBOX per capire come diversi tipi di layout vengono utilizzati in yab.

Se volete trovare uno specifico widget su una view sconosciuta, dovete ciclicamente attraversare le view per trovare quella che contiene il vostro widget. Un simile loop appare come qui (a condizione che MyWidget derivi da BView):

YabView *myView = NULL; 
MyWidget *myWidget = NULL; 
for(int i=0; iCountItems(); i++) 
{ 
     myView = cast_as((BView*)viewList->ItemAt(i), YabView); 
     if(myView) 
     { 
          YabWindow *w = cast_as(myView->Window(), YabWindow); 
          if(w) 
          { 
               w->Lock(); 
               myWidget = cast_as(myView->FindView(id), MyWidget); 
               if(myWidget) 
               { 
                     // do something with myWidget 
                     w->Unlock(); 
                     return; 
               } 
          } 
     } 
} 
Error(id, "MyWidget");
Nota: il return è impostato dopo che qualcosa è stato fatto con il widget e dopo aver sbloccato di nuovo la finestra. Questo consente il controllo degli errori alla fine del ciclo.

3.3   Alcune brevi osservazioni

Alla fine alcune brevi osservazioni su...

    - BUILD macros: Alcuni comandi hanno BUILD macros che consentono di disabilitare intere parti di yab durante la compilazione. Ciò viene utilizzato per produrre codice di dimensioni più contenute con build factory. Le librerie non utilizzate e parti di codice vengono semplicemente tralasciate se non necessarie. Utilizzate queste macro ogni volta che avete a che fare con comandi che li hanno.

    - Drawing: I comandi di Drawing sono un pò complicati. Essi spesso disegnano su una view, su una bitmap o un canvas. Specialmente il view drawing che ha un proprio sistema di archiviazione. Osservate gli altri comandi di disegno per capire come essi lavorano.

    - Own classes: Own classes: Aggiungere proprie classi è bello, ma ricordate di aggiungere queste nuove informazioni anche nel makefile (R5 and ZETA!).

4   Sommario

Per darvi in breve una checklist di ciò che è stato fatto, date un'occhiata al sommario:

    - aggiungere nuove parole di comando (token) in yabasic.flex/i> if necessariamente

    aggiungere la regola del comando (grammar) in yabasic.bison

    aggiungere l'identificatore del comando e il nome del metodo C in yabasic.h

    - aggiungere le chiamate al comando in function.c o main.c

    - aggiungere la funzione wrapper in graphic.c

    - aggiungere C alla funzione wrapper C++ in YabInterface.h e YabInterface.cpp

    - aggiungere il metodo C++ in YabInterface.h e YabInterface.cpp

End

Tutorial created by jan_64 at August 11. 2006

Traduzione di Giuseppe Gargaro
carry over from pdf to html by Christian Albrecht (Lelldorin) September 2006
Made available by BeSly, the BeOS, Haiku & Zeta Knowledgebase.