Sviluppo

Come faccio? - parte III

Gianni Ceccarelli
 

stesura originale in inglese di Eric Slaats - liberamente tradotto da Gianni Ceccarelli
comparso per la prima volta su Os/2 e-Zine! volume 1 numero 14

Benvenuti a questa nuova lezione sulla programmazione PM. In questi articoli saranno discussi semplici problemi di programmazione Presentation Manager. Questi articoli sono indirizzati a persone interessate alla programmazione PM o semplicemente curiose di sapere come funzionano i programmi PM. Per comprendere questi articoli, è utile un po' di esperienza di programmazione (preferibilmente in C++).

Siate pigri!

Questo è in effetti uno dei principi chiave che uso quando programmo. Può anche essere interpretato come: "Non fare ciò che può essere fatto per te!". Quelli della IBM hanno impiegato un sacco di tempo per sviluppare questo sistema operativo e ci si sono impegnati moltissimo. Perché rifare tutto da capo?

Se vi chiedete di cosa stia parlando, facciamo un semplice esempio. Il mese scorso abbiamo scritto un piccolo programma che inseriva un pulsante in una finestra. Per farlo abbiamo semplicemente creato una istanza della classe pulsante. L'unica cosa che abbiamo fatto da noi è stata impostare (o calcolare) la dimensione del pulsante. Potevamo farlo nel modo più difficile e disegnare tutto il pulsante; così facendo ci saremmo ritrovati persi in un mare di chiamate grafiche. La classe pulsante che è definita in OS/2 fa tutto questo al posto nostro.

A parte essere pigro, sono anche a favore del principio "fallo più semplice possibile". Questo mese introduciamo le risorse come un possibile metodo per mantenere il nostro programma semplice e lavorare il meno possibile (ovvero essere prigri!).

Risorse

Non sarebbe bello se non avessimo da scrivere tutte quelle complicate righe di codice C e C++, e bastasse disegnare il nostro programma per poi generarlo? Bene, per alcuni controlli questo è possibile. Possiamo definire i box di dialogo e i menu in questo modo. Sfortunatamente, dobbiamo comunque programmare il modo in cui questi controlli reagiscono agli eventi. Nei prossimi articoli darò molto spazio a questo modo di programmare; per ora ci concentreremo sui menu.

Ma prima descriviamo cosa sono le risorse.

In un file di risorse (.RC) possiamo descrivere i controlli con un semplice linguaggio. Questo non è esattamente un linguaggio di programmazione, quanto piuttosto un linguaggio usato per descrivere come appaiono i controlli. In un file di risorse si possono descrivere ad esempio:

  • Menu
  • Tavole delle scorciatoie
  • Modelli per box di dialogo e finestre
  • Icone
  • Fonts
  • Bitmaps
  • Stringhe

Nella maggior parte dei casi, non dobbiamo preoccuparci di scrivere il file delle risorse a mano. Assieme a molti compilatori, come pure nel Toolkit di OS/2, vengono forniti speciali editor per le risorse. Questi strumenti offrono un ambiente grafico in cui si possono disegnare le risorse che vogliamo usare.

Sfortunatamente, per le risorse di menu di solito non c'è un strumento grafico. Quindi vediamo come si crea un menu. Il modo migliore per spigarlo è fare un esempio:

MENU MAIN
BEGIN
        SUBMENU "Menu ~1", IDM_MENU1
        BEGIN
                MENUITEM "Item 1.~1", IDM_ITEM1_1
                MENUITEM "Item 1.~2", IDM_ITEM1_2
        END

        SUBMENU "Menu ~2", IDM_MENU2
        BEGIN
                MENUITEM "Item 2.~1", IDM_ITEM2_1
                MENUITEM SEPARATOR
                MENUITEM "Item 2.~2", IDM_ITEM2_2
                MENUITEM "Item 2.~3", IDM_ITEM2_3
        END

        MENUITEM "~About", IDM_ABOUT
END
Questo è il menu che aggiungeremo al programma del mese scorso. (Potete trovare queste stesse righe nel file .RC dell'esempio di questo mese). Cerchiamo di analizzarlo.

Se guardate la definizione, dovreste farvi un'idea generale di come appaia il menu. Viene descritto un menu principale con tre elementi, chiamati "Menu 1", "Menu 2" e "About". Se si attiva "Menu 1" o "Menu 2" apparirà un sottomenu, mentre il terzo, "About", è fatto per generare direttamente un evento.

Qualche definizione:

MENU:
L'identificatore MENU è il punto di partenza. Dopo questa parola segue un blocco di definizioni. Tutto assieme forma un menu. Questo blocco deve essere racchiuso tra le parole BEGIN e END.
SUBMENU:
La parola SUBMENU viene usate per dichiarare un sottomenu. Ogni cosa tra BEGIN e END dopo questa parola definisce tale menu. Dentro un sottomenu si possono definire ulteriori sottomenu.
MENUITEM:
Definisce un elemento del menu. Proprio come con SUBMENU, il nome viene inserito subito dopo. Questo è il nome che apparirà nel menu. Notate che in alcuni posti viene usata la ~ (tilde). Ciò può essere fatto solo una volta in ciascun nome. La lettera che segue la tilde sarà sottolineata e potrà essere usata per accedere rapidamente alla voce di menu.
SEPARATOR:
Questa parola è usata in "Menu 2". Se si scrive MENUITEM SEPARATOR, si creerà una linea nel menu. Nel nostro esempio "Item 2.1" e "Item 2.2" saranno separate da una linea.

Con le parole descritte fino ad ora possiamo costruire un menu piuttosto completo. Ci sono comunque molte altre possibiltà, come pulsanti ecc. Queste saranno discusse in un prossimo articolo. C'è un'altra cosa che non ho spiegato a proposito dell'esempio precedente: dopo quasi ogni linea c'è un identificatore tipo IDM_ITEM1_1. Questi sono chiamati identificatori di risorse.

Identificatori di risorse

Il file .RC descrive le risorse, ma il compilatore (e anche il sistema operativo) devono avere un modo per identificare ciascuna risorsa usata da un programma. Perciò ciascuna risorsa ha un identificatore. Un identificatore di risorsa è una costante che di solito viene definita in un file .h (header, intestazione) che viene incluso in tutte le parti del programma che hanno bisogno di conoscere l'identificatore (ID, d'ora in poi). Il file header per il nostro esempio è questo:
#define MAIN           1

#define IDM_MENU1      100
#define IDM_ITEM1_1    101
#define IDM_ITEM1_2    102

#define IDM_MENU2      200
#define IDM_ITEM2_1    201
#define IDM_ITEM2_2    203
#define IDM_ITEM2_3    204

#define IDM_ABOUT      300
Come potete vedere, ciascun identificatore ha un valore diverso dagli altri. Siete abbastanza liberi nello scegliere tali valori. Voci nello stesso menu devono avere numeri diversi. Se invece volete che producano lo stesso evento, dovrete assegnargli lo stesso ID. Il primo file in cui includiamo questo header è il file di risorse (.RC). In questo modo il compilatore delle risorse sa quale ID assegnare alle varie voci. Vogliamo anche usare questo header nel file in cui gestiremo gli eventi prodotti dal menu. Per fare ciò aggiungiamo questa riga ai file simple3.rc e simple3.cpp:
#include "simple3.h"
Perché il file .cpp ha bisogno di conoscere questi ID? Ogni volta che una voce di menu viene attivata, un messaggio WM_COMMAND viene inviato alla finestra. Questo significa che possiamo far intercettare questi messaggi alla window procedure che abbiamo definito per il nostro piccolo programma. Tutto quello che dobbiamo fare nel codice che gestisce questo messaggio è determinare quale voce è stata attivata. Per far ciò usiamo l'identificatore di risorsa. Per capire come funziona, diamo un'occhiata al messaggio WM_COMMAND:
param1   USHORT  uscmd         // Valore del comando

param2   USHORT  ussource      // tipo dell'origine
         USHORT  uspointer     // indicatore del puntatore

returns  ULONG   ulReserved    // valore riservato, dovrebbe essere 0
L'unica cosa che ci serve sapere adesso è come riconoscere l'ID del menu. Per questo ci basta il primo parametro. Il secondo parametro può essere usato per ottenere informazioni sull'origine dell'evento. Per ora, non ci interessa. (Ancora una volta, ne parleremo in un prossimo articolo, perciò continuate a leggere.)

Avrete notato che il primo parametro è un USHORT, perciò non possiamo usarlo semplicemente come ULONG (quale in effetti è, come ogni parametro dei messaggi). Dobbiamo estrarre lo SHORT da questo ULONG. Per fare questo, c'è una piccola macro chiamata SHORT1FROMMP. Guardate questo segmento di codice:

case WM_COMMAND:
     {
     switch (SHORT1FROMMP(mpParm1))
          {
          case IDM_ITEM1_1:
               {
               // Gestiamo l'elemento 1.1
               }
          break;

          case IDM_ITEM1_2:
               {
               // Gestiamo l'elemento 1.2
               }
          break;
          }
     }
break;
Quello che fate nei vari blocchi delle voci di menu dipende dalla vostra applicazione. Nel nostro esempio, useremo la API WinMessageBox per far succedere qualcosa ogni volta che viene premuta una voce di menu. (Questa API mostra un box in cui possiamo far apparire del testo. Esaminate il codice per questo.)

Usare le risorse di menu

A questo punto abbiamo un file di risorse e sappiamo che tipo di messaggi genera il menu e come gestirli. Se lasciamo le cose così, avremo soltanto una finestra senza menu. Quello che ci serve è un modo per collegare il nostro menu alla frame window. E qui saltano fuori i FCF, Frame Creation Flags. Questi sono stati nominati di sfuggita in un precedente articolo e il prossimo mese ne parleremo più a fondo. Tutto quello che dobbiamo sapere per ora è il flag per collegare il menu. Possiamo collegare il menu aggiungendo il flag FCF_MENU.

Dopo aver fatto ciò, dobbiamo dire alla frame window quale risorsa caricare alla sua creazione. Abbiamo chiamato il nostro menu MAIN (che vale 1). Aggiungiamolo al codice di esempio (negli esempi precedenti era impostato a 0).

WinCreateStdWindow (HWND_DESKTOP,     // Genitore
                    WS_VISIBLE,       // Stile (visibile)
                    &flFrameFlags,    // flag di creazione
                    "SampleClass",    // nome della classe
                    "Simple 3 for OS/2 e-Zine!",// Testo del titolo
                    0,                // Client style
                    NULLHANDLE,       // Resource handle
                    MAIN,             // Frame ID  <======= Eccolo!
                    &hwndClient);     // Client handle
Nei prossimi articoli vedremo che tutti i controlli che una frame window deve caricare in seguito ai flag di creazione devono avere un ID pari al Frame ID.

WM_COMMAND e il pulsante

Il messaggio WM_COMMAND viene inviato dalle voci di menu che vengono premute. Ma questo messaggio può anche essere generato da altri controlli, in particolare dal pulsante. Il pulsante invierà un messaggio WM_COMMAND al suo genitore quando viene premuto. Così, se conosciamo l'ID del pulsante, dovremmo essere in grado di intercettare la pressione del pulsante quando gestiamo WM_COMMAND. (Prestate attenzione che l'ID del pulsante non sia uguale a quello di una voce di menu).

Compilare i file .RC

I file .RC devono essere compilati proprio come i file C++. Per fare ciò possiamo usare il compilatore RC che è incluso in OS/2. Con questo compilatore possiamo compilare un file .RC in un file .RES che può poi essere collegato ad un file .EXE (sempre da parte di RC). Molti compilatori possono comunque gestire i file di risorse dall'interno dell'IDE (ambiente integrato di sviluppo). Questo significa che non dobbiamo preoccuparci di fare tutto da riga di comando. (Nel compilatore Borland basta includere il file .RC nel progetto e tutto viene gestito automaticamente).

Note conclusive

Ci sono molte cose che si possono fare con le risorse. Tutto quello che abbiamo fatto è solo una minima parte di ciò che è possibile. Questo significa che vedremo molte altre risorse nei prossimi articoli. Per alcune applicazioni è un modo ideale per creare un programma con un minimo sforzo di programmazione. (le Smalled HTML Extensions sono un esempio: tutto ciò che vedere in quell'applicazione è una risorsa, non c'è neppure un controllo generato dal codice).

Tutti i cambiamenti nel file SIMPLE3.CPP (ZIP, 16.7k) sono marcate con "NEW" per renderle più evidenti.


[Pagina precedente] [Sommario] [Pagina successiva]