Corso di AmigaOS

Torna all'elenco delle lezioniPer dubbi, consigli o richieste, potete mandare un'e-mail ad Andrea Carolfi.
Ringraziamo Amiga Transactor Mailing List per questo tangibile contributo!


I menù (Dodicesima lezione)

In questa lezione cominceremo a vedere i menù. Sono questi strumenti di selezione molto utili e comodi da usare che Intuition mette a disposizione. Oggi esistono tipi di menù anche più comodi di quelli di Intuition, ad esempio i menù popup o i menù contestuali che evitano ogni volta di andare in cima allo schermo per selezionare una o più opzioni/operazioni.
Ma vediamo quello che mette a disposizione Intuition che sono comunque molto utili per la completezza di un programma di una certa dimensione.

Struttura Menu e MenuItem

Prima di introdurre le funzioni della Gadtools che permettono di definire una intera 'menustrip', vediamo nello specifico come sono formate le strutture Menu e MenuItem dichiarate nel file intuition/intuition.h:


/* ======================================================================== */
/* === Menu =============================================================== */
/* ======================================================================== */
struct Menu
{
         struct Menu *NextMenu;    /* Puntatore al prossimo */
         WORD LeftEdge, TopEdge;   /* Posizione del box di selezione */
         WORD Width, Height;       /* Dimensione del box di selezione */
         UWORD Flags;              /* Flags tra quelli definiti sotto */
         BYTE *MenuName;           /* Testo per questo menù */
         struct MenuItem *FirstItem; /* Puntatore alla prima opzione */

         /* Queste variabili dal nome misterioso sono solo per uso interno */
         WORD JazzX, JazzY, BeatX, BeatY;
};


/* FLAGS IMPOSTATI SIA DALL'APPLICAZIONE CHE DA INTUITION */
#define MENUENABLED 0x0001 /* Questo menù è o no abilitato */


Vediamo adesso la struttura MenuItem:

/* ======================================================================== */
/* === MenuItem =========================================================== */
/* ======================================================================== */
struct MenuItem
{
         struct MenuItem *NextItem;   /* Puntatore alla prossima opzione */
         WORD LeftEdge, TopEdge;      /* Position del box di selezione */
         WORD Width, Height;          /* Dimensione del box di selezione */
         UWORD Flags;                 /* Flags definiti più in basso */
         LONG MutualExclude;          /* Bits per la mutua exclusione con altre opz */
         APTR ItemFill;               /* Puntatore a Image, IntuiText, o NULL */

         /* Quando questa opzione è puntata dal cursore e la modalità di evidenziazione
          *   HIGHIMAGE è selezionata, sarà visualizzata questa immagine alternativa
          */
         APTR SelectFill;    /* Puntatore a Image, IntuiText, o NULL */

         BYTE Command;    /* Solo se l'applic imposta il flag COMMSEQ */

         struct MenuItem *SubItem; /* Se non-zero, punta ad un sotto menù */

         /* Il campo NextSelect rappresenta il numero di menù della opzione successiva
          * selezionata (quando l'utente ha selezionato più cose)
          */
         UWORD NextSelect;
};


/* FLAGS IMPOSTATI DALL'APPLICAZIONE */
#define CHECKIT      0x0001   /* L'opzione potrà avre un checkmark */
#define ITEMTEXT     0x0002   /* Se impostato l'opzione è testuale, altrimenti è grafica */
#define COMMSEQ      0x0004   /* Se esiste una short-cut */
#define MENUTOGGLE   0x0008   /* Per la mutua esclusione */
#define ITEMENABLED  0x0010   /* Opzione abilitata */

/* questi sono i significati dei SPECIAL HIGHLIGHT FLAG */
#define HIGHFLAGS 0x00C0   /* see definitions below for these bits */
#define HIGHIMAGE 0x0000   /* Usa l'immagine dell'utente */
#define HIGHCOMP  0x0040   /* Evidenzia complementando il box di selezione */
#define HIGHBOX   0x0080   /* Evidenzia riquadrando il box di selezione */
#define HIGHNONE  0x00C0   /* Non evidenziare */

/* FLAGS IMPOSTATI SIA DALL'APPLICAZIONE CHE DA INTUITION */
#define CHECKED   0x0100   /* Stato del checkmark */
Ora, come potete vedere, definire un certo numero di menù con una certa serie di opzioni, sarebbe un lavoro (e lo era prima di GadTools) davvero oneroso, dovendo valorizzare per ogni singolo menù una struttura Menu e per ogni opzione di ogni menù una struttura MenuItem. La difficoltà maggiore, però la si riscontrava principalmente nella definizione della 'selectbox' (letteralmente la scatola di selezione) che dovrebbe contenere il testo o l'immagine del menù. Infatti il problema principale era che bisognava calcolarsi l'occupazione del testo con il font utilizzato (guai se cambiava, allora sballava tutto) poi lasciare un po di margine sennò tutto veniva appiccicato, ecc. Per non parlare del fatto che il sorgente si allungava parecchio.
Fortunatamente con l'introduzione della gadtools.library le cose sono state di molto facilitate ed automatizzate. Vediamo come.

Struttura NewMenu

Nel file libraries/gadtools.h è dichiarata una struttura NewMenu che serve per la definizione di un'intera 'menustrip'. Come dice il commento:


/* Fill out an array of these and pass that to CreateMenus(): */

basta definire un array di queste e passarla alla CreateMenus.

struct NewMenu
{
         UBYTE nm_Type;         /* Vedi sotto */
         /* Il compilatore inserisce un byte PAD quì */
         STRPTR nm_Label;       /* Etichetta del menù */
         STRPTR nm_CommKey;     /* Scorciatoia da tastiera */
         UWORD nm_Flags;        /* Flags della struttura Menu o MenuItem (vedi note) */
         LONG nm_MutualExclude; /* MenuItem MutualExclude word */
         APTR nm_UserData;      /* Per uso personale, vedi note */
};

/* Necessario solo dentro la definizione di IM_  */
#define MENU_IMAGE   128

/* nm_Type determina a cosa ogni struttura NewMenu corrisponde.
 * Per i valori NM_TITLE, NM_ITEM e NM_SUB, nm_Label dovrebbe essere una
 * stringa di testo da usare per quel titolo di menù, opzione o sotto-opzione.
 * Per IM_ITEM o IM_SUB, imposta nm_Label a puntare ad una struttura Image
 * che desideri usare per questa opzione o sotto-opzione.
 * NOTA: Al presente, puoi solo usare immagini convenzionali.
 * Immagini custom create dalle classi image di Intuition non funzionano.
 */
#define NM_TITLE  1  /* Titolo menù */
#define NM_ITEM   2  /* Opzione menù testuale */
#define NM_SUB    3  /* Sotto-opzione menù testuale */

#define IM_ITEM   (NM_ITEM|MENU_IMAGE) /* Opzione menù grafica */
#define IM_SUB    (NM_SUB|MENU_IMAGE)  /* Sotto-opzione menù grafica */

/* L'array di NewMenu dovrebbe essere terminato con un NewMenu dove
 * nm_Type è uguale a NM_END.
 */
#define NM_END    0  /* Fine dell'array di NewMenu */

/* A partire con la V39, GadTools salterà ogni NewMenu dove il campo
 * nm_Type ha il bit NM_IGNORE impostato.
 */
#define NM_IGNORE 64


/* nm_Label dovrebbe essere una stringa di testo per le opzioni testuali, un
 * puntatore ad una struttura Image per le opzioni di menù grafiche o la
 * costante speciale NM_BARLABEL, per ottenere una barra separatrice.
 */
#define NM_BARLABEL  ((STRPTR)-1)

/* Il campo nm_Flags è usato per riempire o il campo Menu->Flags o
 * MenuItem->Flags.  Notare che il senso dei bit MENUENABLED o
 * ITEMENABLED è invertito tra questo uso e quello di Intuition,
 * in altre parole, i NewMenus sono abilitati per default.  Le etichette
 * seguenti sono provviste per disabilitarli:
 */
#define NM_MENUDISABLED MENUENABLED
#define NM_ITEMDISABLED ITEMENABLED

/* Nuovo per la V39:  NM_COMMANDSTRING.  Per una opzione o sotto-opz testuale,
 * fai puntare nm_CommKey ad una stringa arbitraria ed imposta il flag
 * NM_COMMANDSTRING.
 */
#define NM_COMMANDSTRING COMMSEQ

/* Un puntatore UserData può essere associato ad ogni struttura Menu e MenuItem.
 * La CreateMenus() alloca spazio per un UserData dopo ogni Menu o MenuItem
 * (titolo, opzione o sotto opzione).  Dovresti usare le macro
 * GTMENU_USERDATA() o GTMENUITEM_USERDATA() per estrarlo.
 */

#define GTMENU_USERDATA(menu) (* ( (APTR *)(((struct Menu *)menu)+1) ) )
#define GTMENUITEM_USERDATA(menuitem) (* ( (APTR *)(((struct MenuItem *)menuitem)+1) ) )

Procedure GadTools sui menù

Abbiamo quindi visto la struttura che ci mette a disposizione Gadtools e più o meno dovrebbe essere chiaro come inizializzarla (spero! :-) ). Vediamo adesso quali procedure dobbiamo usare per vedere apparire i nostri menù:

  1. struct Menu *CreateMenusA( struct NewMenu *newmenu, struct TagItem *taglist struct Menu *CreateMenus( struct NewMenu *newmenu, Tag tag1, ... );
  2. void FreeMenus( struct Menu *menu );
  3. BOOL LayoutMenuItemsA( struct MenuItem *firstitem, APTR vi, struct TagItem *taglist ); BOOL LayoutMenuItems( struct MenuItem *firstitem, APTR vi, Tag tag1, ... );
  4. BOOL LayoutMenusA( struct Menu *firstmenu, APTR vi, struct TagItem *taglist ); BOOL LayoutMenus( struct Menu *firstmenu, APTR vi, Tag tag1, ... );

Visto che ormai dovreste aver imparato che differenza c'è tra la versione di funzione con la struct TagItem * e quella con Tag, vediamo a cosa servono.
Sono tutte molto semplici, la 1 serve per allocare ed inizializzare il numero di strutture Menu e MenuItem che serviranno per realizzare i nostri menù.
Tra i tag che accetta ci sono:

  • GTMN_FrontPen (UBYTE) - Colore da usare per il testo dei menù. (def. 0).
  • GTMN_FullMenu (BOOL) - Richiede che l'array di NewMenu sia una completa menustrip e non un frammento. Se viene trovato un frammento, fallirà con un errore secondario di GTMENU_INVALID. (default FALSE).
  • GTMN_SecondaryError (ULONG *) - Puntatore ad una variabile dove memorizzare l'eventuale errore secondario. Possibili errori sono:
    • GTMENU_INVALID - la struttura NewMenu descrive un menù illegale. La CreateMenusA fallirà con risultato NULL.
    • GTMENU_TRIMMED - la struttura NewMenu ha troppi menù, opzioni, o sotto opzioni. La CreateMenusA ritornerà con successo ma con un menù "ritagliato" dalle parti in più.
    • GTMENU_NOMEM - La CreateMenusA ha corso fuori dalla memoria. :-)

Dopo ciò se abbiamo un solo menù possiamo usare la 3 che serve proprio per creare un solo menù, altrimenti se abbiamo una serie di menù dobbiamo usare la 4. I tag che hanno in comune queste due funzioni sono:

  • GTMN_TextAttr (struct TextAttr *) - Carattere da utilizzare per i menù, altrimenti sarà usato quello dell schermo.
  • GTMN_NewLookMenus (BOOL) - Se la finestra è stata aperta con il flag WA_NewLookMenus, si dovrebbe impostare anche questo flag. Informa la GadTools di usare checkmark Amiga-key e colori appropriati.
  • GTMN_Checkmark (struct Image *) - Se stai usando un'immagine custom per il checkmark (WA_Checkmark), passala anche alla GadTools, così può usarla per i menù.
  • GTMN_AmigaKey (struct Image *) - Se stai usando un'immagine custom per l'Amiga-key (WA_AmigaKey), passala anche alla GadTools, così può usarla per i menù.
  • GTMN_FrontPen (ULONG) - Questo tag è esistito anche per la CreateMenus(), ma adesso anche la LayoutMenusA() ce l'ha. Se un numero di penna legittimo è fornito, è usato per colorare le opzioni menù (in preferenza a quello passato alla CreateMenus()). Se GTMN_NewLookMenus è stato specificato, il valore predefinito per questo tag è il BARDETAILPEN dello schermo.
    Per consistenza visuale, noi raccomandiamo di omettere questo tag in tutte le funzioni, e di lasciare che sia usato il default.

Apro una parentesi, non mi do del noi (come il mago Otelma), ho solo tradotto dall'inglese la documentazione della gadtools, dove parla dei tag. :-) Chiusa parentesi.

Il tag che la 3 ha in più rispetto alla 4 è:

  • GTMN_Menu (struct Menu *) - Puntatore alla struttura Menu dove FirstItem è la struttura MenuItem fornita. Se le opzioni di menù sono tali da dover essere incolonnate o spostate, la struttura Menu è necessaria per effettuare il calcolo completo. E' consigliato di fornire sempre questa informazione.

Infine, quando non avremo più bisogno dei menù li potremo liberare in una botta con la 2.

Il discorso è lungi dall'essere finito, infatti occorre sapere come collegare un menù ad una finestra, come toglierlo, come dis/abilitare le opzioni e come ottenere i menù selezionati dall'utente.
Per collegare o rimuovere una 'menustrip' ad/da una finestra ci vengono incontro

  • BOOL SetMenuStrip( struct Window *window, struct Menu *menu ); void ClearMenuStrip( struct Window *window );

che mi pare non abbiano bisogno di ulteriori spiegazioni.

Per dis/abilitare un'opzione di menù, una sotto-opzione, un menù intero ecco le:

  • void OffMenu( struct Window *window, unsigned long menuNumber );
  • void OnMenu( struct Window *window, unsigned long menuNumber );

per quanto riguarda il discorso sul secondo paramero conviene rifarsi alla lezione nove, quando ho descritto come vengono identificati i menù in un numero a sedici bit.

Nel sorgente allegato (non è più parte integrante della lezione, visto che è sempre lo stesso che cresce), le funzioni spiegate fanno bella mostra di sè creando un menù per il momento fittizio. Più in là, con le prossime lezioni, contribuiremo a renderlo più "vivo".

Lezione precedente Indice delle lezioni Lezione successiva
Copyright AMiWoRLD Ph0ton