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ù (Tredicesima lezione)

In questa lezione concluderemo il discorso sui menù di Intuition. Nella puntata precendente abbiamo visto la loro creazione e rimozione, in questa vedremo la loro gestione.
Per poter "ascoltare" i messaggi che ci invia un menù, dobbiamo impostare tra i flag IDCMP della nostra finestra, IDCMP_MENUPICK.
Fatto questo, nel ciclo di gestione degli eventi, andremo ad aggiungere il caso specifico IDCMP_MENUPICK.
Ma il messaggio IDCMP_MENUPICK è generico, occorre sapere oltre dell'avvenuta selezione di un menù, di quale opzione si tratta. Questo è possibile, operando sul campo Code della struttura IntuiMessage (roba vecchia... siamo preparati laggiù in fondo? Metti via i dadi Bonetti! :-))) ) vediamo come.

IntuiMessage e menù

Come vi avevo spiegato un pò di lezioni fa, Intuition "impacchetta" il numero di menù, opzione e sotto opzione in un numero a 16bit che restituisce nel campo Code della struttura IntuiMessage ogni qualvolta l'utente seleziona un menù (sempre che sia stato deciso di ascoltare questo tipo di evento).
E' compito del programmatore, "spacchettare" il suddetto numero a 16bit nella informazione richiesta. Fortunatamente Intuition o meglio, i suoi programmatori, ci vengono incontro fornendoci alcune macro per le operazioni di impacchettamento e spacchettamento. Queste sono:

  • #define MENUNUM(n) (n & 0x1F)
  • #define ITEMNUM(n) ((n >> 5) & 0x003F)
  • #define SUBNUM(n) ((n >> 11) & 0x001F)

  • #define SHIFTMENU(n) (n & 0x1F)
  • #define SHIFTITEM(n) ((n & 0x3F) << 5)
  • #define SHIFTSUB(n) ((n & 0x1F) << 11)

  • #define FULLMENUNUM( menu, item, sub ) \ ( SHIFTSUB(sub) | SHIFTITEM(item) | SHIFTMENU(menu) )

Un ciclo di gestione potrebbe quindi essere:

while(!finito)
{
   WaitPort(win -> UserPort);
   while((msg = GT_GetIMsg(win -> UserPort)))
   {
      switch(msg -> Class)
      {
          case IDCMP_CLOSEWINDOW:
                   finito = TRUE;
           break;
           case IDCMP_MENUPICK:
                switch(MENUNUM(msg -> Code))
                {
                     case 0:
                          switch(ITEMNUM(msg -> Code))
                          {
                               case 0:
                                    Printf("1a opzione del 1o menù!\n");
                               break;
                               case 1:
                                   Printf("2a opzione del 1o menù!\n");
                               break;
                               case 2:
                                    Printf("3a opzione del 1o menù!\n");
                               break;
                               case 3:
                                    Printf("4a opzione del 1o menù!\n");
                               break;
                               case 4:
                                    Printf("5a opzione del 1o menù!\n");
                               break;
                          }
                     break;
                     case 1:
                             Printf("Secondo menù!\n");
                     break;
                     case 2:
                             Printf("Terzo menù!\n");
                     break;
                }

[...]

Un altro modo potrebbe essere:

while(!finito)
{
     WaitPort(win -> UserPort);
     while((msg = GT_GetIMsg(win -> UserPort)))
     {
          switch(msg -> Class)
          {
               case IDCMP_CLOSEWINDOW:
                       finito = TRUE;
               break;
               case IDCMP_MENUPICK:
                       switch(msg -> Code)
                       {
                               case FULLMENUNUM(0,1,NOSUB):
                                       Printf("1a opzione 1o menù!\n");
                               break;
                               case FULLMENUNUM(1,1,NOSUB):
                                       Printf("2a opzione 2o menù!\n");
                               break;
                       }

[...]

Entrambi i modi sono equivalenti, il primo è forse più didattico ma prolisso, il secondo più compatto. Comunque, il secondo metodo può essere ulteriormente raffinato grazie alle comode define, ad esempio:

#define PROGETTO_SALVA  FULLMENUNUM(0,3,NOSUB)
#define OPZIONI_CARICA  FULLMENUNUM(2,1,NOSUB)

[...]

case IDCMP_MENUPICK:
        switch(msg -> Code)
        {
                case PROGETTO_SALVA:
                        /* Devo salvare il progetto */
                        SalvaProgetto();
                break;

                [...]

                case OPZIONI_CARICA:
                        /* Devo caricare le opzioni da disco */
                        CaricaOpzioni();
                break;

                [...]

Che mi sembra molto leggibile.

In questo discorso di MENUNUM ITEMNUM e altro, rientrano pienamente le funzioni OnMenu e OffMenu che avevo introdotto nella lezione precedente senza dargli un approfondimento maggiore. In questa lezione, spiego che, come il nome lascia intendere, la OffMenu permette di disabilitare e quindi rendere inacessibile un opzione di menù o anche un intero menù che potranno essere abilitati con la controparte della funzione, OnMenu.
In questo modo, se chiamo OffMenu(win,PROGETTO_SALVA), disabiliterò la quarta opzione del primo menù che potrò riabilitare con la OnMenu(win,PROGETTO_SALVA).

Rimane da spiegare, per chi ha dato un'occhiata al sorgente allegato , cosa sono quegli strani ~(1 << 5). Penso che tutti sapranno che è uno shiftamento a sinistra di 1 di 5 posizioni e di una negazione, ma credo che non tutti avranno capito a cosa serve. Avrete certamente notato che solo tre opzioni hanno questo valore impostato che non è nemmeno lo stesso e che sono gli unici ad avere i flag MENUTOGGLE e CHECKIT abilitati. Questo valore infatti serve se vogliamo creare una serie di opzioni in mutua esclusione tra di loro; come nell'esempio tra IFF, Testo e 8SVX. Il valore indica a quali opzioni togliere il simbolo di vista quando quell'opzione è selezionata. Infatti, nel caso di IFF, lo shiftamento di 1 di 5 posizioni indicherebbe proprio l'opzioni IFF (la sesta di quel menù), ma creando il NOT, diventa: tutte le altre tranne quella.
Ovviamente, il sistema rimuoverà il check solo a quelle opzioni di quel menù che hanno il flag MENUTOGGLE abilitato.

Infine, abbiamo la funzione ItemAddress. Questa funzione serve per ricevere un puntatore alla struttura MenuItem dell'opzione corrispondente al parametro passato.

struct MenuItem *ItemAddress(struct Menu *menu,UWORD menunumber);

Per esempio, chiamando ItemAddress(win -> MenuStrip,PROGETTO_SALVA), mi restituirà il puntatore alla struttura MenuItem relativa alla quarta opzione del primo menù. Inoltre nella gestione degli eventi per gestire la multi selezione. Vi ricordate il campo NextSelect della struttura MenuItem?
Ebbene con questa funzione potete ricavarvi quello dell'opzione selezionata e se NextSelect è diverso da MENUNULL, passare a gestire il prossimo menù selezionato. In questo modo:

case IDCMP_MENUPICK:
{
        UWORD code = msg -> Code;
        do
        {
           struct Menuitem *item;
           switch(code)
           {
                   case PROGETTO_SALVA:
                           /* Devo salvare il progetto */
                          SalvaProgetto();
                   break;

                   [...]
           }

           item = ItemAddress(win -> MenuStrip,code);
           code = item -> NextSelect;
    }
    while(code != MENUNULL);

    [...]

o in uno equivalente che preferite.

Con questo mi sembra di aver concluso quello che c'era da dire sui menù, ormai crearli, gestirli e deallocarli non dovrebbe più essere un segreto per voi.
Con i rudimenti che vi ho introdotto in queste tredici lezioni dovreste essere già in grado di creare piccole applicazioni, magari non rivolte al pubblico ma per i vostri scopi personali. Quando poi si arricchiranno di file requester e requester, avrete in mano le nozioni per scrivere un'applicazione base.

La prossima puntata vedremo ancora un pò di funzioni di Intuition e GadTools su finestre schermi e bottoni e requester. Quella dopo faremo un'excursus veloce sulla asl.library.

Listato di questa lezione

Lezione precedente Indice delle lezioni Lezione successiva
Copyright AMiWoRLD Ph0ton