Gli script di Jaws 06. Finestre, Controlli, Funzioni definite dall’Utente, Suoni.

Programmiamoli da Soli!

Dove siamo?

Questa che ci siamo appena posti non è una delle classiche domande esistenziali, bensì uno dei più frequenti quesiti che Jaws si pone quando lo attiviamo con dei tasti, o quando il sistema operativo lo innesca generando un qualche evento nel PC. Gran parte del suo lavoro, infatti, dipende dall’ambiente in cui deve agire: se si trova in una certa situazione, deve fare una cosa; se invece si trova in un’altra, la sua azione può essere diversa oppure nulla.

Il contesto generale in cui si svolge tale ricerca è la finestra attiva in quel momento.

Lo scopo principale di questo capitolo sarà proprio quello di capire meglio cosa sono le finestre, nonché imparare come farci spiegare da Jaws com’è configurato il sistema, in modo da potervi adattare i nostri script.

A tal fine, completeremo l’analisi dei possibili elementi di codice, realizzando finalmente delle funzioni da noi definite. Da questo capitolo in poi il loro uso sarà costante, rappresentando il vero salto di qualità nelle nostre personalizzazioni del codice.

Il capitolo si concluderà con un argomento più leggero, parlando della gestione dei Suoni in Jaws, con uno script di esempio che speriamo troverete simpatico.

Prima di iniziare con le finestre, tuttavia, ancora un po’ di pazienza, perché dobbiamo svolgere quell’attività che abbiamo definito necessaria all’inizio delle parti operative dei vari capitoli, stavolta coinvolgendo anche quello che dal momento della sua creazione non abbiamo più aperto.

Esercizio 6.1.1. Aggiornare il file Personale delle Costanti.

Se ricordate, il file delle costanti si chiama
_PsConst.JSH
, e di sicuro dovrete ricordarvi come cercare il file, perché si tratta di una procedura che daremo per acquisita. A partire dalla prossima occasione, non porremo neppure più queste informazioni preliminari. Solo per stavolta, infine, ricordiamo di inserire le assegnazioni qui proposte prima dell’ultima costante assegnata.


CORRENTE = 1, ; valore per il livello corrente delle finestre
REALE = 2, ; valore per il livello reale delle finestre
PRINCIPALE = 3, ; valore per il livello principale delle finestre
ATTESA = 2, ; decimi di secondo per i quali sospendere il flusso
BELL = "\7", ; codice di controllo omonimo, Ascii 7
CR = "\R", ; Carattere Carriage Return, Ascii 13
RIGA_VUOTA = 2, ; valore che indica di creare una riga vuota
INIZIO_C = 66, ; base per conversioni da numeri in lettere partendo dalla lettera C maiuscola
INIZIO_A = 59, ; base per conversioni da numeri in lettere partendo dalla lettera A maiuscola

Esercizio 6.1.2. Aggiornare il file Personale delle Variabili Globali.

Ricordando che questo file si chiama
_PsGlobals.JSH
, suggeriamo anche qui di destinare come ultima l’assegnazione registrata alla creazione del file,
gnTempoIniziale
, ed inserire quelle che noi proporremo d’ora in poi appena prima di questa, così da poterne confermare, in ognuna di esse, il carattere Virgola finale.


String gsNomeFinestra, ; nome del tipo di finestra rilevata

Esercizio 6.1.3. Aggiornare il file Personale dei Messaggi.

Anche qui diamo per scontata la procedura di ricerca ed apertura del file dei Messaggi,
_PsMessages.JSM
. Allo stesso modo, ricordiamo soltanto di inserire le assegnazioni sottostanti prima della riga di chiusura con
EndMessages.


; voci di scelta per il livello delle finestre
@lstLivelliFinestra
Corrente|Reale|Principale
@@
; base per titolo nella selezione da un elenco di voci
@ttlSelezione
%1 %2. Frecce seleziona, Invio conferma, Esc annulla.
@@
; titolo per la scelta del livello delle finestre
@ttlLivelloFinestra
Scegliere il livello a cui leggere i dati nella finestra
@@
; riepilogo dei dati sulla finestra
@msgDatiFinestra
Dati sulla Finestra %1.
Handle. %2
. Control ID. %3
. Nome. %4
. Tipo. %5
. Codice di Tipo. %6
. Codice di Sottotipo. %7
. Classe. %8
%9
@@
; suffisso per informazioni nel Visualizzatore Virtuale
@hlpFineInfo
Premere Escape per chiudere questo messaggio.
@@
; base per due termini separati da un trattino
@msg2Trattino
%1-%2
@@
; base per tre termini separati da trattini
@msg3Trattino
%1-%2-%3
@@
; due termini seguiti dal carattere Punto
@msg2Punto
%1. %2.
@@
; nome base per file audio con note musicali
@lstFileNote
Piano%1-%2.WAV
@@

***

Le Finestre e la loro gerarchia.

Entriamo ora nel tema principale del capitolo dedicato alle
Finestre
, che sono così importanti da dare loro stesse il nome all’intero sistema operativo.

Per questo , dentro a Windows, i programmi vengono eseguiti all’interno di finestre, e persino JAWS possiede una sua finestra. Anche ogni campo di immissione, pulsante e altro controllo sono finestre, o almeno lo dovrebbero essere.

Come tutte le altre regole dei programmi di computer, infatti, anche questa non viene sempre seguita. Un programmatore può scegliere di creare controlli, come i campi di immissione testo, senza renderli vere e proprie finestre.

Nella maggioranza delle applicazioni, tuttavia, ogni finestra di dialogo, menu, pulsante, editazione, visualizzazione ad elenco, eccetera, costituisce una finestra separata. Ciascuna finestra possiede uno o più numeri di identificazione, i quali possono essere utilizzati per riferirsi a essa.

Questi valori non vengono messi a caso, ma sono collegati tra loro tramite una gerarchia speciale, che permette al sistema operativo, ed ai programmatori che lavorano al suo sviluppo, di tenere traccia di tutte queste finestre.

Per spiegare la gerarchia delle finestre nel sistema, la metafora che viene più spesso utilizzata è quella di genitrice e figlie, dove il Desktop del computer è la genitrice di tutte le applicazioni Windows. Questo fa sì che le finestre di JAWS, dell’elaboratore di testo e del browser Internet siano tutte figlie del Desktop. Queste finestre si trovano tutte un livello sotto al Desktop, quindi sono tutte figlie del Desktop allo stesso livello logico.

Pensate alla struttura ad albero delle directory in Esplora Risorse, ipotizzando che la vostra unità disco sia identificata con la lettera
C
. In questo caso, il percorso
C:\
è la genitrice di tutte le altre directory che si trovano in quell’unità.

Per questo, tutte le sottodirectory che si trovano in basso di un livello rispetto a
C:\
sono figlie di quella genitrice, e si trovano allo stesso livello logico.

Ad esempio, la cartella Windows, o la cartella programmi, che sono a questo primo livello, a loro volta saranno le genitrici di tutte le cartelle che sono dentro ad esse.

La struttura genitrice/figlia delle finestre di Windows funziona in modo molto simile alla struttura delle directory. Ogni applicazione costituisce un ramo dell’albero, e da esso vengono generate le finestre figlie.

Come identificare le finestre.

Per distinguerle le une dalle altre, il programmatore dell’applicazione assegna un numero di identificazione a ciascuna finestra. Ognuna di queste, inoltre, possiede a sua volta alcune categorie di dato che possono servire ad identificarla.

Jaws riesce a conoscere questi dati tramite apposite funzioni integrate, di cui elencheremo le principali. In tutti i casi, la categoria del dato sarà la parte restante del nome della funzione, dopo il prefisso
Get
, (Ottieni), ed il secondo termine
Window
, (Finestra). Nella traduzione, che segue come sempre il termine inglese, la categoria del dato sarà invece la sua parte centrale.

  1. GetWindowName
    , (OttieniNomeFinestra), che fornisce un nome, come ad esempio quello del documento aperto, nell’Editor di Script o in in un programma di videoscrittura.
  2. GetWindowType
    , (OttieniTipoFinestra), che restituisce sempre un dato testuale, ma stavolta sul tipo impostato.
  3. GetWindowTypeCode
    , (OttieniCodiceTipoFinestra), che invece restituisce un valore numerico sul codice del tipo.
  4. GetWindowSubtypeCode
    , (OttieniCodiceSottotipoFinestra), anch’esso con ritorno numerico circa il codice di sottotipo.
  5. GetWindowClass
    , (ottieniClasseFinestra), che restituisce un dato testuale sulla classe assegnata alla finestra.

Senza dilungarci troppo su ciascuna categoria, si può dire innanzi tutto che nessuna di esse è univoca, e che quindi nessuno di questi dati può indicarci da solo di quale finestra si tratti. Per usarle negli script, il sistema può essere quello di servirci di più categorie per incrociarne i dati, così da avere un’identificazione più certa della finestra su cui si trova il cursore.

Esaminando tali categorie, il nome della finestra è persino troppo specifico, in quanto fornisce anche particolari come il citato nome del documento aperto. Se non ci può essere utile adesso, lo sarà invece più avanti, quando sfrutteremo proprio questa sua caratteristica per acquisire informazioni di dettaglio.

Restando sui dati testuali, il tipo di finestra o non è specificato, oppure risulta troppo generico.

I codici di Tipo e di Sottotipo, che sono invece dei valori numerici, corrispondono ad oltre un centinaio di costanti memorizzate nel file predefinito HJConst.JSH. Questa classificazione numerica è stata pensata per poter poi convertire i dati in informazioni adattate per le varie lingue, ma il risultato è che spesso i due dati coincidono.

Tornando ai dati testuali, rimane la classe finestra, che appare il dato più significativo. Ad essa, infatti, spesso i programmatori assegnano un nome specifico, esclusivo nella maggior parte dei casi.

Quest’ultimo tipo di dato, se da un lato ci aiuta a capire che si tratti proprio di quella finestra, dall’altro potrebbe non far capire in particolare allo Screen-Reader cosa sia e cosa si possa fare al suo interno. Per questo motivo, Jaws mette a disposizione lo strumento per riassegnare le classi delle finestre, attivabile con
TastoJaws+7 della tastiera estesa
, che consente a ciascuno di abbinare eventuali nomi non standard ad uno di quelli invece riconosciuti dal programma di sintesi vocale.

Prima di passare ai soliti esempi pratici, tuttavia, analizziamo altre due categorie di dati, forniti per orientarsi nel mare magnum delle finestre di Windows.

Il Codice identificativo ed il numero progressivo.

Ci sono altri due dati che Jaws ci restituisce di quelli usati dai programmatori per gestire le finestre:

  1. Control ID
    , (IdentificatoreControllo).
  2. Handle
    , (Maniglia) o (Appiglio).

Il primo, così come abbiamo anticipato nel titolo, è un codice identificativo assegnato dai programmatori agli elementi dell’applicativo, che si rileva tramite la funzione
GetControlID
, (OttieniIdentificatoreControllo).

Questo dato dovrebbe essere univoco, almeno nell’ambito di quella applicazione, ma molti programmatori lo lasciano con valore zero, e quindi non fornendo tramite esso nessuna informazione. C’è poi anche qualche caso in cui esso varia ad ogni chiamata dell’applicazione, come ad esempio nelle nuove versioni dell’Editor di Jaws, rendendolo perciò poco utile ai nostri scopi.

In ogni caso, siccome Jaws lavora su tutte le applicazioni, non vi sarebbe ugualmente alcuna certezza che lo stesso
Control ID
non sia usato da due diverse applicazioni aperte nello stesso momento. Così, per evitare confusioni, il sistema assegna anche un numero progressivo a qualsiasi finestra che viene aperta, l’Handle appunto.

Esso è un valore che rimarrà abbinato ad una specifica finestra per tutto il tempo in cui l’applicazione cui appartiene risulterà attiva. Poi, la volta successiva che l’applicazione sarà chiamata, la stessa finestra avrà un Handle diverso, ma sarà comunque riconosciuta in maniera assolutamente univoca dal sistema.

Per spiegarsi meglio, è la stessa cosa di quando si va in un negozio dotato dei bigliettini numerati per stabilire l’ordine in cui si è serviti: ciascuna persona ha un nome e cognome, un
Control ID
, e poi prende un bigliettino con il numero progressivo, l’
Handle.

Se quella stessa persona il giorno dopo torna in quel negozio, il suo Control ID sarà ovviamente lo stesso, mentre l’Handle molto probabilmente sarà diverso. Tuttavia, in entrambe le occasioni, grazie all’Handle, la persona sarà identificata univocamente tra le altre, così come capita alle finestre nel sistema.

L’Handle, inoltre, pur essendo un dato numerico, viene considerato da Jaws come un dato a parte, che si chiama proprio
Handle
e che ha come lettera identificativa del dato la
h
. Quindi, quando si dovranno creare delle variabili per registrare degli Handle, la sintassi sarà:


Var Handle hNomeVariabile

Interroghiamo le finestre.

Jaws fornisce una serie di strumenti per aiutare i realizzatori di script a conoscere i dati relativi alle finestre ed agli elementi che vi sono contenuti, attivando la modalità
HomeRow
, (FilaDiCasa), tramite la pressione di
-Insert+Windows+Meno
del tastierino numerico). Tale modalità somiglia a quella dell’Aiuto Tastiera, nel senso che dopo averla attivata le funzioni dei tasti cambiano, così da aiutare la navigazione tra gli elementi delle finestre.

Per chi volesse avere informazioni su come muoversi in questa modalità, una volta attivata, premendo
TastoJaws+H
sarà visualizzata una schermata di informazioni sui tasti operanti in tale modalità.

Per gli obiettivi che ci siamo prefissi, tuttavia, non è indispensabile studiare ora questi strumenti. Può essere sufficiente, almeno per il momento, dotarci di script informativi da noi realizzati, così anche per far pratica nell’utilizzo di qualche nuova funzione.

Prima di iniziare a parlare del codice va ricordato, quando prima si parlava di gerarchia delle finestre, che ciascuna di esse è inserita in una sequenza che prevede qualche finestra precedente, e talvolta anche delle finestre successive. Quando si interroga la finestra, pertanto, la prima domanda da porsi è quale sia il livello della finestra di cui si vogliono conoscere i dati.

Abbiamo già accennato nello scorso capitolo alla differenza tra il risultato di
GetCurrentWindow ()
, la quale restituisce l’Handle della finestra dove è posto il cursore attivo, e
GetFocus ()
, che invece ci fornisce l’Handle di quella con il focus del sistema, a prescindere da dove sia posto il cursore attivo. Per questo, se a noi serve soltanto sapere genericamente dove si trova il cursore attivo, potremo usare la prima, mentre se ci servono informazioni più dettagliate, è opportuno usare la seconda.

Va detto, per chiarezza, che i risultati di
GetCurrentWindow ()
e
GetFocus ()
spesso coincidono, ma ugualmente bisogna fare una distinzione tra i diversi utilizzi.

Ad esempio, c’è un’altra importante funzione,
GetRealWindow
, (OttieniFinestraEffettiva), che risale tra i vari livelli della finestra corrente alla ricerca della prima che abbia un titolo. Siccome questa funzione richiede come parametro proprio il sapere da quale finestra far partire la sua ricerca, spesso si usa a tale scopo
GetFocus ().

Oltre a queste due opzioni, tra i vari livelli di finestra che possono esserci utili, può essere necessario risalire sino alla finestra principale dell’applicazione attiva, il cui Handle si ottiene dalla funzione
GetAppMainWindow
, (OttieniFinestraPrincipaleApplicazione). Anche questa opzione richiede un Handle di partenza e, pure in questo caso, il più delle volte si usa
GetFocus ()
come parametro.

Ricapitolando, i livelli della finestra che a noi serve conoscere sono tre:

  1. Corrente
    , che otterremo con la funzione
    GetCurrentWindow ().
  2. Reale
    , che invece otterremo con l’istruzione
    GetRealWindow (GetFocus ()).
  3. Principale
    , che infine avremo grazie all’istruzione
    GetAppMainWindow (GetFocus)).

Ottimizzare gli script tramite le funzioni.

Capita spesso, man mano che si producono nuovi elementi, che si possa finire col scrivere delle parti di codice simili tra loro, o persino del tutto identiche. Le funzioni definite dall’Utente, quelle che ciascuno di noi può crearsi, servono anche a questo: spostare parti di codice ripetitivo dentro a funzioni che possono essere chiamate più volte, e per mezzo di altre parti del codice stesso.

Inoltre, come avremo modo di esaminare, l’uso di proprie funzioni consente di snellire il codice e renderlo più leggibile. Grazie ai parametri che potremo impostare in ciascuna di esse, poi, riusciremo a creare degli strumenti molto efficaci e versatili.

Un altro dei grandi vantaggi che si hanno con le Funzioni è quello di poterle scrivere all’interno dello script Predefinito, anche se utilizzate prioritariamente per qualche script locale. In altre parole, se per esempio noi dovessimo scrivere una funzione che serve per uno script dentro al Blocco Note, nel progettarla potremo cercare di capire se, scrivendola in un certo modo o aggiungendoci qualche ulteriore parametro o funzionalità, la si possa anche condividere ponendola nel file script Predefinito.

Così facendo, oltre che metterla a disposizione di tutti i nostri script, si riesce a concentrare il nostro codice personalizzato nel minor numero possibile di file, facilitando così le operazioni di modifica, revisione o aggiunta codice.

Breve ripasso sulle funzioni.

Stiamo ancora imparando, un po’ alla volta, a conoscere ed utilizzare le funzioni integrate di Jaws. In ogni caso, adesso è per noi più facile capire anche come si faccia a crearle, ma appare opportuno fare un breve riepilogo delle principali caratteristiche delle funzioni:

  1. Una funzione di solito restituisce un risultato, che si può utilizzare direttamente o registrare in una variabile. Vi sono tuttavia anche funzioni che non restituiscono nulla, che sono di tipo
    Void
    , (Vuoto), come ad esempio le funzioni che si limitano a spostare il cursore.
  2. Ad una funzione si può passare nessuno, uno o più parametri, a seconda delle esigenze. Tale impostazione va effettuata durante la procedura guidata che serve per creare le funzioni, ma si può aggiornare anche in un secondo momento, con delle limitazioni che analizzeremo.
  3. I parametri sono di norma obbligatori, nel senso che spesso è necessario siano indicati quando si effettua una chiamata a funzione. Vi sono tuttavia anche dei parametri opzionali, e anche questi è possibile prevederli nelle funzioni da noi definite.
  4. Se la quasi totalità delle funzioni esegue il suo lavoro direttamente nel codice, vi sono anche delle funzioni speciali che richiedono una nostra scelta. E proprio con queste proseguiamo questa prima parte teorica del capitolo.

Le funzioni interattive.

Ci sono delle funzioni di Jaws che sono esse stesse degli script, in quanto arrestano il flusso dell’elemento di codice in cui sono inserite ed attendono che l’Utente esegua delle azioni. Una volta compiute tali azioni, il controllo ritorna all’elemento dal quale sono state chiamate, ed il flusso riprende.

La prima di queste di cui ci occupiamo è La funzione
DlgSelectItemInList
, (DialogoSelezioneVociInElenco), che consente appunto di selezionare con le frecce una delle possibili opzioni proposte a video, per poi confermarla con Invio oppure annullare l’operazione premendo Escape.

La funzione ha ben sei parametri possibili, ma di questi solo tre obbligatori, gli unici di cui ci occuperemo in questa occasione, e che elenchiamo di seguito:

  1. L’elenco delle voci testuali da selezionare, separate tra loro da un carattere speciale, che può essere quello denominato
    Pipe
    , l’Ascii 124, oppure il carattere di controllo chiamato anche
    Bell
    , l’Ascii 7.
  2. Un testo che si vuole costituisca il titolo della finestra di dialogo visualizzata dalla funzione, quello che si legge con il comando
    TastoJaws+T.
  3. Un valore, TRUE o FALSE, che indica se le voci dell’elenco debbano essere visualizzate in ordine alfabetico, oppure come sono state inserite nel primo parametro.

Questa funzione ha come ritorno il valore numerico che corrisponde al numero progressivo della voce selezionata, e poi confermata con Invio, oppure uno zero se si esce con Escape.

Sull’argomento, dopo le brevi note, resta da dire soltanto che proprio questo strumento costituirà da solo il contenuto della nostra prima funzione. Avremo quindi modo, realizzandone un esempio immediato, di comprenderne meglio il suo utilizzo.

Per tornare quindi alla pratica, dopo le pillole di teoria, iniziamo esaminando nel dettaglio quale dev’essere d’ora in poi il nostro programma di lavoro.

Prima le funzioni, poi gli script.

Quando si salva e compila un file script, tutti gli elementi di codice, Script e Funzioni, si mescolano assieme e sono tutti a disposizione di Jaws e delle sue chiamate.

L’unico ordine tassativo che si deve rispettare in un file script è quello delle dichiarazioni, sia delle variabili locali dentro agli elementi, sia quelle di tipo
Include
per indicare i file esterni dove dichiarare variabili globali, costanti o messaggi. Tali dichiarazioni, infatti, devono essere poste nel file script prima che il codice inserito le richieda, altrimenti la compilazione segnalerà di non aver trovato ciò che era necessario.

In genere, tuttavia, quando si realizza una procedura che prevede degli elementi di codice che sono chiamati da altri elementi, ad esempio uno script che chiama delle funzioni da noi realizzate, è comunque utile scrivere nell’ordine, dall’inizio alla fine, prima le funzioni e poi gli script. Questo perché, se non altro, qualora si intenda utilizzare la procedura di inserimento guidato, quella che si attiva con
Control+I
, per poter inserire negli elementi successivi una funzione, o uno script, devono essere ovviamente già presenti e regolarmente compilati.

Per questo, quando si mette in cantiere una procedura che preveda elementi di codice di diverso tipo, è necessario fare un’attenta progettazione, nella quale si analizzi nel dettaglio la successione delle chiamate a funzione all’interno dello script. Poi, sulla base di tale analisi, si inizierà a scrivere la funzione che per prima sarà chiamata dallo script, quindi l’eventuale seconda funzione, e così via, per poi mettere assieme il codice dello script vero e proprio come ultimo passo della procedura.

In particolare, come analisi preventiva, noi dovremo elencare le varie fasi dello script, cercando di capire quali tra queste saranno eseguite dallo script stesso, o da funzioni integrate di Jaws, e quali invece saranno affidate a funzioni che noi creeremo per l’occasione.

Come al solito, un esempio pratico sarà certamente più chiaro.

Leggere i dati delle finestre.

Sulla base di quanto detto sinora, le fasi della procedura che andremo a realizzare saranno le seguenti:

  1. Scegliere a quale livello si devono leggere i dati della finestra, con una prima funzione da noi realizzata,
    SceltaLivello ().
  2. Determinare quale sia l’Handle del livello di finestra scelto, tramite una seconda funzione da noi realizzata,
    LivelloFinestra ().
  3. Estrarre il nome del livello scelto, usando la funzione
    StringSegment ().
  4. Costruire il testo della schermata con i dati, utilizzando la funzione
    FormatString ().
  5. Leggere e presentare i dati a video, tramite la funzione
    SayMessage ().

Come avete potuto notare, solo nelle prime due fasi sono presenti funzioni da noi realizzate, e che scriveremo dunque nell’ordine in cui sono state presentate.

Siccome è la prima volta che creiamo una nostra funzione, riprendiamo una procedura passo passo che possa guidarvi in questa nuova esperienza.

Esercizio 6.2.9. La nostra prima funzione, SceltaLivello.

FileScript.

Default.JSS

Nome.

SceltaLivello

Descrizione.

Selezionare il livello della finestra di cui leggere i dati.

Novità.
  1. La funzione integrata
    DlgSelectItemInList ()
    , appena illustrata.
  2. La nostra variabile globale
    gsNomeFinestra
    , che riprenderemo più avanti.
Note.

Per creare una funzione , all’interno del file script Predefinito, portatevi come sempre alla fine del file, in una riga vuota, e premete la stessa scelta rapida usata per creare gli script,
Control+E.

Da qui, seguite questi passi:

  1. Nel primo campo, come al solito, immettete il nome scelto, che come anticipato nel titolo, sarà
    SceltaLivello.
  2. Nel secondo campo, non premete la barra spazio, così come si faceva negli script, bensì premete Tab per passare oltre. Con questa semplice omissione direte alla procedura guidata che intendete scrivere una funzione, anziché uno script, e questa poi vi proporrà dei campi diversi da quelli incontrati sinora.
  3. Il campo Sommario sarebbe lo stesso degli script ma, diversamente da questi, non è rilevante in quanto il suo contenuto non viene utilizzato direttamente dall’Editor. Quindi, anche qui passate oltre senza fare nulla.
  4. Il campo Descrizione, invece, è lo stesso che negli script, e va compilato perché il suo contenuto sarà letto nelle fasi di inserimento guidato delle funzioni, allo stesso modo di quelle integrate di Jaws. In questo caso, il consiglio è quindi quello di inserire certamente qualcosa, come ad esempio
    "Sceglie il livello della finestra di cui leggere i dati.".
  5. Anche il campo Categoria ha la stessa valenza che negli script, e quindi valgono gli stessi suggerimenti forniti in proposito.
  6. Le novità iniziano dal sesto campo, che in questo caso si chiama
    Ritorni funzione
    , dove appunto si deve specificare se vi sia, e di quale tipo possa essere, il risultato delle funzioni. Si dovrà scegliere da un elenco, con i vari tipi di dati disponibili. Nel nostro caso selezioniamo
    Int
    , per
    Integer
    , il tipo numerico.
  7. L’ultimo campo presente,
    Descrizione Ritorni
    , come dice il nome, serve a inserire un breve testo che sarà poi visualizzato anch’esso durante le fasi di inserimento funzioni. Proprio per questo, inserite anche qui un testo come ad esempio
    "Il valore della scelta effettuata.".
  8. A questo punto, non essendoci nel nostro caso parametri da impostare, premete
    Alt+F
    per concludere l’inserimento.

Una volta tornati alla schermata principale dell’Editor, la struttura della funzione creata avrà la seguente prima riga d’intestazione:


Int Function SceltaLivello ()

Dopo alcune righe vuote, come nel caso degli script, vi sarà un analogo comando di chiusura:


EndFunction

Questa volta, ad inizio della riga d’intestazione, è posto il tipo di ritorno, quello che avevamo specificato
Int
. Esso è seguito dalla parola chiave
Function
, (Funzione), che serve per indicare a Jaws di che tipo di elemento del codice si tratti.

Il nome della funzione, poi, appare seguito dalla solita coppia di parentesi aperta e chiusa, come per gli script, perché la funzione non possiede parametri. Nella prossima funzione da creare, che invece ne sarà dotata, avrete modo di capire come i parametri siano visualizzati all’interno di queste due parentesi.

Per concludere l’analisi, l’istruzione di chiusura,
EndFunction
, (FineFunzione), determina soltanto il punto in cui si conclude l’elemento di codice. Così come per gli script, essa è un’istruzione a sé stante, che non necessita di specificare altro.

Ora, per quel che riguarda il contenuto da esprimere, resta da chiarire come fa la funzione a restituire quello che si chiama
ritorno
, il dato che è il risultato dell’elaborazione del codice.

Abbiamo già incontrato l’istruzione
Return
, che vuol dire appunto Ritorno, e che sinora era stata utilizzata da sola, per fermare il flusso dello script. Ebbene, alla stessa istruzione, se vi si pone alla sua destra un testo, un valore numerico o un qualsiasi altro risultato di un’altra funzione, tale testo o valore numerico viene restituito come risultato della funzione stessa.

Così, nel codice che poniamo di seguito, la funzione appena descritta,
DlgSelectItemInList ()
,è appunto preceduta dall’istruzione Return, in quanto è il risultato stesso della funzione integrata a fornire il valore numerico che sarà quello restituito dall’intero elemento di codice.

A tal proposito, così come avevamo già fatto con altri elementi complessi, porremo la chiamata della funzione su più righe per facilitarne la comprensione. Nel dettaglio, la prima riga di codice con la sola assegnazione del ritorno della funzione stessa, e le altre tre destinate agli altrettanti parametri.

Come ultima nota, segnaliamo che nel terzo parametro della funzione
FormatString ()
, la quale crea il testo da proporre a video nella finestra di scelta del livello, appare la variabile globale
gsNomeFinestra
. Si tratta di un elemento impostato ad inizio capitolo nel nostro relativo file personale, che ci servirà però solo più avanti, ma che inseriamo ora per portarci avanti col lavoro.

Codice.


Int Function SceltaLivello ()
Return DlgSelectItemInList (; restituisce la scelta effettuata
lstLivelliFinestra, ; parametro1, stringa memorizzata nei messaggi
FormatString (ttlSelezione, ttlLivelloFinestra, gsNomeFinestra), ; parametro2, testo a video
FALSE); parametro3, valore per disattivare l’ordinamento alfabetico
EndFunction

Collaudo.

Se la compilazione va a buon fine, il test di una funzione è possibile solo se la stessa è attivata da uno script, da un evento del PC o da un’altra funzione. Per questo , intanto proseguiamo con il prossimo elemento da creare, e poi con lo script collauderemo la procedura tutta assieme.

Esercizio 6.2.10. La funzione LivelloFinestra.

FileScript.

Default.JSS

Nome.

LivelloFinestra

Descrizione.

restituire un valore che indica di quale finestra rilevare i dati, sulla base delle scelte operate in precedenza.

Novità.
  1. Le nostre costanti
    CORRENTE,
    REALE
    e
    PRINCIPALE
    , equivalenti ai valori 1, 2 e 3, che servono per dare un numero progressivo ai tre livelli possibili a cui rilevare i dati.
  2. Le funzioni integrate
    GetRealWindow (),
    GetFocus ()
    e
    GetAppMainWindow ()
    , già illustrate in precedenza, che servono a rilevare i dati per i tre citati livelli.
Note.

Malgrado la semplicità di questa funzione, seguiamo nel dettaglio la sua procedura solo perché si tratta della prima volta che ne affrontiamo una con i parametri. Quindi, dopo aver avviato la procedura guidata con
Control+E
, seguite questi passi.

  1. Nel primo campo, il nome da dare è
    LivelloFinestra.
  2. Premete
    Alt+D
    per passare direttamente al campo Descrizione, dove mettete qualcosa come
    "Restituisce l’Handle del livello di finestra scelto."
    . Se volete, compilate anche il campo Categoria, e poi passate oltre.
  3. Nel campo
    Ritorni funzione
    , selezionate
    Handle
    , perché questo è il tipo di dato da restituire, poi passate al campo successivo per specificare qualcosa nella
    Descrizione Ritorni
    , come ad esempio
    "La finestra di cui leggere i dati.".
  4. Compilato il contenuto della prima scheda, passiamo a specificare i parametri. Per visualizzare l’apposita pagina, premete una volta la combinazione
    Control+Tab,
  5. Qui, all’ingresso sarete su un elenco dei parametri, che sarà vuoto. Premete Tab per spostarvi nel campo
    Nuovo Parametro
    , dove si dovrà dare il nome della variabile che fungerà da parametro nella funzione. Scrivete quindi
    iScelta.
  6. Qui, premendo una volta Tab, si andrebbe sul campo
    Per riferimento
    , che si può attivare tramite la barra spazio. Più avanti, al primo esempio che lo consentirà, spiegheremo in dettaglio di che cosa si tratti, ma per il momento non ci serve e quindi passiamo oltre.
  7. Premete un’altra volta Tab, oppure direttamente
    Alt+D
    , per andare al campo descrizione, dove porre appunto una breve nota sul parametro appena impostato . In questo caso, scrivete
    "Il valore numerico della scelta effettuata."
    . Poi, premete Tab.
  8. Nel campo
    Tipi Disponibili
    , selezionate dall’elenco la voce
    "Int"
    . Quindi, premete Tab.
  9. Giunti sul pulsante
    Aggiungi
    , premete Invio per confermare il parametro. Fate attenzione a non andare in là di un Tab e cliccare sul pulsante
    Ok
    , perché questo chiuderebbe sì l’inserimento, ma senza salvare il parametro appena immesso.
  10. Una volta premuto il pulsante di Aggiunta, si tornerà quindi all’inizio, nel campo
    Nuovo Parametro
    , dove, nel caso ve ne fossero di ulteriori, si potrebbe iniziare nuovamente la procedura per indicarli. Al termine dei parametri da specificare, come in questo caso, premendo Invio, o cliccando sul pulsante
    Ok
    , si chiuderà l’intera procedura di creazione.

Tornati alla schermata principale dell’Editor, provate ad analizzare l’intestazione della funzione appena creata:


Handle Function LivelloFinestra (int iLivello)

I dati sono posti nel solito ordine, con il tipo di ritorno che precede la parola chiave
Function
. L’unica cosa a cambiare è appunto il parametro immesso, posto tra la coppia di parentesi.

In particolare, il nome della variabile che rappresenta il parametro è preceduto dal tipo di dato, così come si farebbe in una dichiarazione di variabile. Di fatto, i parametri sono anche questo, perché nel corpo della funzione i nomi di variabili si possono usare nel codice, appunto, come se fossero stati dichiarati.

Un’ultima annotazione, che avremo modo di verificare più avanti: nel caso di una serie di più parametri, ciascun parametro con la propria dichiarazione sarà separato dall’altro tramite un carattere Virgola.

Eccoci quindi arrivati al contenuto della funzione . In questo caso, poiché le opzioni possibili sono tre, come i livelli elencati in precedenza, tre saranno anche le relative istruzioni
Return
che avranno alla loro destra il risultato da restituire, in questo caso quello delle relative funzioni integrate di Jaws che rilevano l’Handle della finestra.

Tre saranno anche le costanti, impostate in precedenza, ciascuna corrispondente ad un valore progressivo dei livelli da 1 a 3, e che saranno poste come termini di paragone per le condizioni.

Codice.


Handle Function LivelloFinestra (int iLivello)
If iLivello == CORRENTE Then; se stata scelta la prima voce,
Return GetCurrentWindow (); restituisce l’Handle della finestra corrente
ElIf iLivello == REALE Then; se invece si è scelta la seconda voce,
Return GetRealWindow (GetFocus ()); restituisce l’Handle per la prima finestra col titolo
ElIf iLivello == PRINCIPALE Then; Se invece è stata scelta l’ultima voce,
Return GetAppMainWindow (GetFocus ()); restituisce l’Handle della finestra Principale
EndIf; fine controllo livello
EndFunction

Collaudo.

Anche qui, limitatevi a salvare compilando. Se tutto va bene, dedichiamoci finalmente a ciò che ci permetterà di effettuare la prova dell’intera procedura.

Esercizio 6.2.11. Lo script InfoFinestra.

FileScript.

Default.JSS

Nome.

InfoFinestra

Sommario.

Presenta i dati della finestra.

Descrizione.

Legge e pone a video una panoramica sui dati della finestra del tipo selezionato.

TastiAttivazione.

Shift+Control+Windows+I

Novità.
  1. La nostra prima funzione
    SceltaLivello
    , senza parametri, che consente di scegliere a quale livello di finestra leggere i dati.
  2. La nostra seconda funzione
    LivelloFinestra
    , che ha come parametro
    iScelta
    , di tipo
    Int
    . Questa assegna il valore
    Handle
    del livello di finestra da leggere.
  3. Le varie funzioni integrate che servono per rilevare i singoli dati, che abbiamo già analizzato nel dettaglio, e che troverete nel codice poste una dopo l’altra.
  4. La nostra costante
    PIPE
    , che corrisponde al separatore delle voci per l’elenco dei livelli della finestra.
  5. La costante nativa
    OT_USER_BUFFER
    , che corrisponde al valore 40, la quale invia il testo specificato al
    Visualizzatore Virtuale
    , il già citato ambiente a tutto schermo di Jaws.
Fasi.
  1. Si inizia col cancellare la variabile globale gsNomeFinestra, qualora sia stata impostata. Ribadendo che per il momento essa non sarà coinvolta, tale cancellazione serve soltanto ad impedire che nel testo a video della funzione
    SceltaLivello ()
    , compaiano dei dati non adatti a questa fase della procedura.
  2. Vi sarà poi la scelta del livello di finestra di cui leggere i dati, chiamando la funzione
    SceltaLivello ()
    che abbiamo creato.
  3. Un primo controllo verificherà quindi che non sia stato premuto un tasto per annullare l’operazione, Esc o Annulla, cosa che attiverebbe l’istruzione Return, e con essa la conclusione dello script.
  4. Se lo script procede, si chiama la seconda funzione da noi realizzata,
    LivelloFinestra ()
    , che assegna l’Handle ad una variabile.
  5. Sempre sulla base della voce scelta, viene estratto il titolo che sarà utilizzato come primo parametro nella formattazione della schermata di dati.
  6. La schermata delle informazioni viene formattata inserendo i dati rilevati dalle singole funzioni di rilevamento dati, che sono state poste ciascuna come un parametro per la funzione, e conclusa dalla formula di chiusura, anch’essa inserita come ulteriore parametro.
  7. Il testo viene inviato al
    Visualizzatore Virtuale
    , tramite
    SayMessage ()
    , e lo script si conclude.
Note.
  1. Per facilitare la comprensione del codice qui riportato, i blocchi con le dichiarazioni , le istruzioni e le strutture di controllo sono state separate tra loro con una riga vuota. Inoltre, le funzioni sono state spezzate, creando una riga per la funzione in sé ed una ciascuna per tutti i suoi parametri, anch’essi commentati:
  2. La scelta di inviare il testo con i dati al
    Visualizzatore Virtuale
    di Jaws, è data dalla possibilità in questo ambiente di potersi muovere liberamente, selezionando e copiando il testo presente a video.
  3. In questo caso, noi avremmo da un lato la necessità di formattare i vari dati su un messaggio base, dall’altra quella di usare una funzione di lettura che sfrutta i
    Tipi di Output
    .Malgrado ciò, non possiamo servirci della funzione
    SayFormattedMessage ()
    , la quale fonde queste due azioni, perché essa non ha effetto quando si inviano le informazioni al
    Visualizzatore Virtuale
    . Per questo, stavolta siamo costretti ad usare in sequenza le due diverse funzioni che svolgono questo compito,
    FormatString ()
    e
    SayMessage ()
    . Più avanti, invece, capiremo come avere un approccio molto più completo e versatile a questo ambiente a tutto schermo.

Codice.


Script InfoFinestra ()
Var
int iScelta, ; scelta effettuata
Handle h, ; finestra da leggere
String sLivello, ; livello della finestra
String sInfo; testo del messaggio
Let gsNomeFinestra = NULLO; azzera l’eventuale valore impostato
Let iScelta = SceltaLivello (); imposta il livello della finestra di cui leggere i dati
If !iScelta Then; nel caso la variabile sia 0, essendo stato premuto il tasto Esc o Annulla,
Return; interrompe lo script
EndIf; fine controllo annullamento
Let h = LivelloFinestra (iScelta); imposta l’Handle, sulla base del livello appena scelto
; estrae il nome del livello della finestra e lo imposta nella variabile
Let sLivello = StringSegment (lstLivelliFinestra, PIPE, iScelta)
Let sInfo = FormatString ( ; crea il testo da visualizzare mettendo i dati nei segnaposto
msgDatiFinestra, ; parametro1, nome del messaggio base
sLivello, ; parametro2, testo della voce scelta, al posto di %1,
h, ; parametro3, Handle, al posto di %2
GetControlID (h), ; parametro4, Control ID, al posto di %3
GetWindowName (h), ; parametro5, nome della finestra, al posto di %4
GetWindowType (h), ; parametro6, tipo della finestra, al posto di %5
GetWindowTypeCode (h), ; parametro 7, codice di tipo, al posto di %6
GetWindowSubtypeCode (h), ; parametro 8, codice di Sottotipo, al posto di %7
GetWindowClass (h), ; parametro9, classe della finestra, al posto di %8
hlpFineInfo); parametro10, formula di fine informazioni, al posto di %9
SayMessage (OT_USER_BUFFER, sInfo); invia i dati al Visualizzatore Virtuale
EndScript

Collaudo.

Dopo averlo salvato e compilato, provate a vedere se ci sono differenze, e quali siano, tra le tre modalità di lettura dei dati all’interno delle finestre:
CORRENTE,
REALE
e
PRINCIPALE.

Ora che sappiamo quali siano i dati relativi alla finestra, e come recuperarli tramite le funzioni, nel resto del capitolo analizzeremo una serie di metodi per sfruttare queste conoscenze.

***

Creare un codice identificativo per le Finestre.

Si è già detto che nessuno dei dati rilevabili, preso singolarmente, può dirci con assoluta certezza in quale finestra ci si trovi. Così come si era accennato, per raggiungere tale obiettivo si potrebbero usare più dati assieme, così da creare una sorta di nostro codice identificativo personalizzato.

Una volta definito questo codice, sarà sufficiente confrontarlo con quello di volta in volta rilevato, con lo stesso metodo, tramite uno speciale controllo nei nostri script. Tale controllo, costituito essenzialmente da una struttura
If
Then
, confronta i dati e, nel caso siano diversi, non essendo perciò nella finestra desiderata, interrompono il flusso dello script, altrimenti continuano ad eseguirlo.

Quali e quanti dati sono sufficienti a garantirci un codice univoco? La risposta più semplice sarebbe tutti, ma in realtà qualcuno dobbiamo escluderlo per non appesantire troppo il controllo da effettuare.

Iniziamo con l’eliminare il Tipo di Finestra, non tanto perché poco significativo, bensì perché di tipo testuale e, qualche volta, anche molto lungo.

Per quel che riguarda i due codici numerici, quello di Tipo e di Sottotipo, li utilizzeremo nel primo livello di finestra, anche se il loro valore può essere posto a zero, oppure modificato nel tempo riassegnando le classi finestra, con la già citata combinazione
Insert+7.

Il nome della finestra, invece, è un dato non sempre presente, ma comunque che può essere utile in alcune situazioni. Nel dettaglio, quando si analizza una finestra a livello
Corrente
, ad esempio in una finestra di editazione come l’Editor di Script, il nome della finestra comprende anche il nome del file, e quindi non può essere usato perché rende troppo specifici i dati che potremmo inserire nel codice identificativo. Se invece ad essere analizzata è una schermata sotto a quella principale come, ad esempio, la finestra di dialogo
Trova
, se la leggiamo al livello che abbiamo definito
Reale
il
nome
può essere anche l’unico che differenzia da altre schermate dello stesso tipo.

Restano poi altri due dati: la
Classe Finestra
,che useremo in ogni caso, ed il
ControlID
, che invece non useremo affatto, poiché spesso a zero e, come detto, in qualche caso neppure univoco.

Ricapitolando, dovendo usare codici con una forma diversa, sulla base del livello al quale si rilevano i dati, potremo avere tre tipi di codice identificativo, corrispondenti ad altrettanti termini che servano per identificare anche il livello utilizzato.

  1. Corrente
    , con un codice formato dai valori di
    Tipo
    e di
    Sottotipo
    ,e della
    Classe Finestra.
  2. Reale
    , ancora con la
    Classe Finestra
    , preceduta in questo caso dal
    Nome
    della finestra stessa.
  3. Principale
    , con gli stessi dati del tipo precedente.

A questo punto, appare utile occuparsi di un esercizio pratico per far capire meglio l’argomento trattato.

Un esempio di Codice Identificativo.

La maggior parte degli script che abbiamo realizzato sinora, e buona parte di quelli che creeremo d’ora in poi, sono destinati a lavorare in un ambiente di editazione. Tra le varie finestre di
Editing
, così come si traduce in inglese, quella in cui stiamo lavorando di più è senza dubbio quella dell’Editor di Jaws: da qui partiremo, dunque, con un primo esempio di codice identificativo, individuato tramite una funzione da noi realizzata, e che poi registreremo in una costante.

Per creare materialmente questo
Codice identificativo
, portatevi nella finestra di editazione dell’Editor di Script, quindi seguite questi passi:

  1. Eseguite lo script InfoFinestra, premendo
    Shift+Control+Windows+I
    , se avete seguito le nostre indicazioni.
  2. Scegliete il livello
    CORRENTE
    , e premete Invio.
  3. Quando compare la schermata, risalite fino a raggiungere le righe con i tre dati che ci interessano:
    • Codice di Tipo.
    • Codice di Sottotipo.
    • Classe.

Annotiamoci, dunque, questi tre dati, copiando le altrettante righe adiacenti negli appunti. Precisiamo che, purtroppo, la prima riga dovrà essere copiata nel modo classico, selezionandola e poi premendo il comando di copia
Control+C
, perché il nostro script
CopiaRiga ()
in questo ambiente non funziona.

La seconda e la terza riga, invece, potranno essere aggiunte alla prima con il nostro script
AggiungeRiga ()
, premendo la combinazione
Shift+Control+TastoJaws+FrecciaSu.

I dati da voi raccolti dipendono dalla vostra configurazione, in particolare dal sistema operativo del vostro pc. In ogni caso, questi dovrebbero essere:

  1. Codice di Tipo
    , un codice numerico di una o due cifre.
  2. Codice di Sottotipo
    , un altro codice numerico analogo al precedente.
  3. Classe
    , un dato testuale.

Il nostro codice identificativo dovrà quindi contenere questi tre dati, separati ciascuno da un carattere Trattino,
.

Li lasceremo anche in quest’ordine, sia pure mettendoli in un’unica riga, all’inizio il Codice di Tipo, poi quello di Sottotipo, ed infine la Classe Finestra.

Questi dati, almeno per il momento, li registreremo in una costante, cui daremo il nome
CODICE_EDITOR
, la quale avrà dunque questo schema di assegnazione:


CODICE_EDITOR = "nn-nn-ttttt", ; codice di Editing nell’Editor di Jaws

In questo esempio,
"nn"
sta per i valori numerici, e
"ttttt"
per quello testuale.

Aprite dunque il vostro file delle costanti,
_PsConst.JSH
, ed inserite manualmente l’assegnazione, con i dati da voi rilevati. Come al solito, vi suggeriamo di inserirla come penultima riga del documento, prima della costante
NULLO.

Salvate il file, quindi chiudetelo e tornate all’Editor.

Ora dovremo predisporre una funzione che non sarà legata ad uno script in generale, ma sarà di supporto ai controlli che potremo inserire in tutti quegli script che lo necessitano.

Prima, una doverosa precisazione per capire come continuare il lavoro.

Istruzioni per le Funzioni sotto forma di scheda.

Nelle nostre prime due Funzioni, quelle
"senza"
e
"con"
parametri, abbiamo analizzato le analogie e le differenze che esistono tra gli Script e le Funzioni. Ora, così come abbiamo fatto per i primi, anche per le seconde daremo d’ora in poi le informazioni sotto forma di scheda, con le varie sezioni che saranno come al solito presenti solo se avranno dei contenuti.

Confermando che anche in questo caso saranno omesse le indicazioni per la Categoria, l’elenco delle schede per le Funzioni sarà il seguente:

  1. FileScript
    , il nome del file in cui dovrà essere inserita la funzione.
  2. Nome
    , il nome da immettere nel campo omonimo della procedura guidata.
  3. Descrizione
    , l’azione in dettaglio svolta dalla funzione.
  4. Ritorni funzione
    , il tipo di dato restituito dalla funzione, con delle brevi note su cosa rappresenti.
  5. Parametri
    , con elencati gli eventuali parametri della funzione, ciascuno su una riga contenente dapprima il nome, quindi l’eventuale indicazione se classificarlo
    per riferimento
    , quindi una breve descrizione ed infine il tipo di dato del parametro, seguita dall’eventuale indicazione del parametro opzionale.
  6. Novità
    , una descrizione di eventuali nuovi concetti o funzioni che la funzione contiene.
  7. Fasi
    , i principali passi compiuti dalla funzione, qualora ve ne sia più d’uno.
  8. Note
    , eventuali annotazioni suppletive circa il suo funzionamento.
  9. Codice
    , il codice vero e proprio da inserire.
  10. Collaudo
    , eventuali informazioni sulla prova della procedura realizzata. Qualora questa sezione sia omessa, si dà per scontato che ci si limiti a salvare il nostro codice compilandolo, e così rinviando le azioni di collaudo al primo script utile.

Esercizio 6.3.3. La funzione CodiceFinestra.

FileScript.

Default.JSS

Nome.

CodiceFinestra

Descrizione.

Leggere i dati della finestra aperta in quel momento, al livello indicato come parametro, per restituirne il codice identificativo personale.

Ritorni.

Di tipo String. Il codice identificativo della finestra al livello specificato.

Parametri.
  1. iLivello. Il valore del livello di finestra di cui leggere i dati. Di tipo Int.
Fasi.
  1. Determinare l’Handle della finestra di cui rilevare i dati, tramite la funzione
    LivelloFinestra ()
    a cui si passa il valore ricavato dal parametro
    iLivello.
  2. Tramite una struttura di controllo, decidere se il messaggio da creare sarà a due o tre termini.
  3. Restituire il codice identificativo, ottenuto formattando il risultato delle funzioni necessarie ai due tipi di codice, tramite l’uso di
    FormatString ().
Note.

Ad inizio capitolo abbiamo impostato due messaggi, tra gli altri, dal nome
msg2Trattino
e
msg3Trattino
, i quali servono appunto ad affiancare due o tre termini, che andranno sostituiti ai relativi segnaposto, separandoli tramite un carattere Trattino. Formattando i dati di nostro interesse con queste basi, quindi, otterremo una stringa adatta al confronto, nel nostro caso, con il codice identificativo che noi abbiamo rilevato manualmente, e registrato nella costante
CODICE_EDITOR
. Se volete, tornate indietro nel capitolo, o aprite il nostro file Messaggi, per controllare direttamente la forma delle due basi da noi predisposte.

Codice.


String Function CodiceFinestra (int iLivello)
Var Handle h; l’Handle della finestra
Let h = LivelloFinestra (iLivello); determina l’Handle sulla base del livello indicato
If iLivello == CORRENTE Then; se si è richiesto un codice del primo tipo,
Return FormatString ( ; restituisce un messaggio formattato con tre termini
msg3Trattino, ; parametro1, messaggio base con tre segnaposti
GetWindowTypeCode (h), ; parametro2, codice di tipo, al posto di %1
GetWindowSubtypeCode (h), ;parametro3, codice di Sottotipo, al posto di %2
GetWindowClass (h));parametro4, classe della finestra al posto di %3.
ElIf iLivello == REALE ; Se invece si è scelto il livello Reale,
|| iLivello == PRINCIPALE Then; oppure quello principale,
Return FormatString ( ; restituisce un messaggio formattato con due soli termini
msg2Trattino, ; parametro1, messaggio base con due segnaposti
GetWindowName (h), ; parametro2, nome della finestra da mettere al posto di %1
GetWindowClass (h));parametro3, classe della finestra al posto di %2.
EndIf; fine controllo livello
EndFunction

Collaudo.

  1. Salvate compilando e, come spesso accade con le funzioni, rimandiamo al primo script utile il test sul nostro lavoro.
  2. Per completare questo blocco di Elementi, dovremo mettere mano al primo lavoro del capitolo, ma soltanto dopo aver aver creato un nuovo elemento che vi andrà inserito per tale modifica.

Esercizio 6.3.4. La funzione VociScelta.

FileScript.

Default.JSS

Nome.

VociScelta

Descrizione.

Compone le voci da proporre a video nella funzione di scelta del livello, aggiungendo al nome del livello anche i relativi esempi di codice per la finestra da elaborare.

Ritorni.

Di tipo String. L’elenco delle voci da visualizzare nella finestra di dialogo.

Novità.
  1. La nostra funzione
    CodiceFinestra ().
  2. La costante
    ATTESA
    , equivalente al valore 2, che corrisponde al numero dei decimi di secondo per i quali si sospende il flusso. Tale sospensione è necessaria per garantire una corretta lettura dei dati relativi alla finestra.
  3. La costante
    BELL
    , (Campanello), che contiene il carattere di controllo omonimo, l’Ascii 7. Come già si accennava, tale carattere speciale è l’altro separatore usato da Jaws quando crea degli elenchi tramite le sue funzioni, oltre al
    Pipe
    che abbiamo già usato nello scorso capitolo. Il suo uso è indicato quando, come in questo caso, vi è la possibilità teorica che il testo delle voci da separare possa contenere un carattere
    Pipe
    , creando quindi confusione qualora fosse usato quest’ultimo come carattere di separazione.
Note.
  1. La funzione è costituita da un unico ciclo
    For
    , che dapprima estrae il nome del livello della finestra a cui leggere i dati, allo stesso modo in cui si faceva direttamente nella nostra funzione
    SceltaLivello ()
    , aggiungendovi poi anche un esempio di codice di finestra, tramite la chiamata dell’omonima funzione da noi appena realizzata.
  2. Il fatto di aggiungere un esempio del codice di finestra accanto al nome del livello da scegliere, come si può intuire, serve appunto a darci ulteriori elementi per capire quale livello sia adatto alla lettura dei dati della finestra. In generale, sarebbero da evitare tutti i livelli che includano nel codice eventuali elementi variabili, come ad esempio i nomi dei documenti aperti negli applicativi.

Codice.


String Function VociScelta ()
Var
Int i, ; contatore del ciclo
String sVoci, ; elenco da restituire
String sNome; nome del livello di finestra
Delay (ATTESA); sospende il flusso per i decimi di secondo impostati
For i = 1 To StringSegmentCount (lstLivelliFinestra, PIPE); scorre le voci nella stringa
If i > 1 Then; se si è dal secondo passaggio in poi,
Let sVoci = sVoci + BELL; inserisce il separatore delle voci
EndIf; fine controllo passaggi
Let sNome = StringSegment (lstLivelliFinestra, PIPE, i); estrae la prima parte della voce
Let sVoci = sVoci + FormatString (msg2Punto, sNome, CodiceFinestra (i)); accoda il codice
EndFor; fine ciclo di scansione livelli
Return sVoci; restituisce l’elenco ricostruito
EndFunction

Esercizio 6.3.5. La versione definitiva di SceltaLivello ().

FileScript.

Default.JSS

Nome.

SceltaLivello

Novità.
  1. La nostra funzione
    VociScelta ()
    , appena creata.
Note.
  1. L’aggiornamento della funzione si limita a sostituire la seconda riga del codice, corrispondente al parametro iniziale, immettendo la seguente parte d’istruzione:
  2. 
    VociScelta (), ; parametro1, la nostra funzione che compone le voci di scelta
    

Codice.


Int Function SceltaLivello ()
Return DlgSelectItemInList (; restituisce la scelta effettuata
VociScelta (), ; parametro1, la nostra funzione che compone le voci di scelta
FormatString (ttlSelezione, ttlLivelloFinestra, gsNomeFinestra), ; parametro2, testo a video
FALSE); parametro3, valore per disattivare l’ordinamento alfabetico
EndFunction

Collaudo.

  1. Se la compilazione va a buon fine, come già detto nel primo elemento di questo blocco, il collaudo arriverà più tardi, al primo script che chiamerà tali funzioni.
  2. Prima di arrivarci, tuttavia, proponiamo un breve argomento di carattere generale, che poi sarà ripreso in seguito, quando realizzeremo la procedura finale di questo capitolo.

***

La conversione tra Script e Funzioni.

In precedenza avevamo fatto un riepilogo sulle funzioni, elencandone gli aspetti principali. Ora qui facciamo un confronto tra funzioni e script, analizzandone le differenze.

In particolare, rispetto agli Script, le Funzioni in Jaws:

  1. Hanno una differente parola chiave nell’intestazione, e come suffisso all’istruzione di chiusura,
    Function
    anziché
    Script.
  2. Non possono essere attivate direttamente da combinazioni tasti, ma devono essere chiamate dall’interno di script o di altre funzioni.
  3. Possono restituire un risultato come ritorno, il cui tipo sarà specificato nell’intestazione prima della parola chiave
    Function.

Per completezza dell’argomento, va detto che i parametri non sarebbero di per sé un’esclusiva delle funzioni, poiché in teoria anche negli Script è possibile specificarli. Tuttavia, se nel caso delle Funzioni tale utilizzo è pressoché costante, per gli script è talmente raro che li si può quasi aggiungere all’elenco delle differenze.

Sul piano pratico, qualora ve ne fosse la necessità, per convertire uno Script in Funzione, o viceversa, si deve soltanto agire sul già più volte utilizzato comando
Visualizza Documentazione
, richiamabile con la scelta rapida
Control+D.

Qui, andando nel campo
Può essere assegnato al tasto
, ed attivando o disattivando la selezione con la Barra Spazio, si passerà ciclicamente da Script a Funzione. Sulla base del fatto che questo secondo campo della schermata sia attivato oppure no, il resto dei campi sarà proposto a video a seconda delle due modalità.

Va detto che questo passaggio dall’uno all’altro elemento di codice non è sempre indolore, in quanto nella conversione qualche dato potrà andare perduto, come ad esempio le impostazioni della Categoria o la descrizione nei parametri. Si tratta tuttavia di un’operazione davvero poco frequente, ed in ogni caso sarà sufficiente ripassare i vari campi per capire quali dati eventualmente reinserire.

Dopo questa breve parentesi, che riapriremo a breve, affrontiamo dunque la procedura che ci consentirà di realizzare un primo controllo sulle finestre.

Gestire la scrittura di testo tramite una funzione.

Se ricordate, in precedenza abbiamo analizzato alcuni aspetti su come ottimizzare gli script tramite le funzioni. Ora, ne realizzeremo un esempio pratico, rielaborando un paio di script e copiandone uno per ricavarne da esso, appunto, una interessante funzione.

In due momenti successivi, nel Capitolo 3 e poi nel Capitolo 5, abbiamo realizzato due diversi script,
InserisceFirma ()
e
AperturaCodice ()
, che svolgono un compito sostanzialmente uguale. Entrambi, infatti, scrivono del testo nel documento aperto, con la differenza che il primo ne scrive soltanto una riga, mentre il secondo almeno quattro.

Ora, il nostro lavoro si svolgerà in queste fasi:

  1. Faremo una copia dello script più complesso,
    AperturaCodice ()
    , e la convertiremo in una funzione che possa scrivere tutti i tipi di testo ad essa inviati.
  2. Svuoteremo il contenuto di entrambi gli script, riducendolo ad un’unica riga di codice con una chiamata della funzione appena realizzata.
  3. Doteremo la funzione, e quindi entrambi gli script, di un controllo che subordini la scrittura del testo al fatto di trovarsi nella finestra di editazione dell’Editor di Script.

Come al solito, partiremo dalla funzione, che ci riserva gran parte del lavoro, poi torneremo indietro a modificare anche gli script.

Aggiorniamo il contenuto di una funzione.

Abbiamo già esaminato sotto l’aspetto formale cosa comporti in Jaws convertire il codice tra Script e Funzioni, elencando le varie differenze e chiarendo le procedure necessarie. Ora resta solo da aggiungere che nei casi di conversione, in particolare quando si deve passare da uno script ad una funzione, la possibilità che quest’ultima sia chiamata da altri elementi di Jaws, costringe a valutare se e come ampliare l’efficacia del codice, così da poter rispondere anche ad esigenze diverse.

Questo è il caso dell’elemento che ci apprestiamo a modificare, dove da un codice composto da una quindicina di righe della versione originale, si passa a circa il doppio in quella convertita in funzione. Questo è dovuto ad una serie di accorgimenti e strutture di controllo , che la conversione ha costretto ad aggiungere proprio per aumentarne la versatilità.

Di seguito presenteremo in sequenza tali modifiche, sia per far capire di cosa si tratti, sia per fornire altre informazioni di carattere generale. Lo scopo di queste precisazioni non è voler entrare nel dettaglio a tutti i costi, bensì fornire risposte soltanto a chi si ponga delle domande.

Per chi appartiene a quest’ultima categoria, quindi, si consiglia di procedere all’esame delle note sottostanti. Per gli altri, potete anche saltare le informazioni supplementari, passando direttamente alla scheda con l’esercizio.

  1. "Aggiunta di un contatore per le righe."
    Quando una funzione deve trattare dei segmenti di testo come fossero righe, è necessario utilizzare una variabile di tipo
    Integer
    che funga da contatore, la quale calcoli i ritorni a capo effettivamente inseriti. Tale contatore sarà usato nella funzione per sommare tutte le nuove righe create, dentro o fuori dal ciclo principale.
  2. "Rimozione del carattere di Ritorno a Capo, Ascii 13, se presente."
    Nella gran parte dei file testuali, come ad esempio il file Messaggi da cui avevamo preso il testo da scrivere nella versione originale, il carattere che indica la fine di una riga è l’Ascii 10,
    NuovaLinea
    , o LineFeed. Nel trattare le stringhe testuali, tuttavia, anche Jaws a volte rileva testi con ritorni a capo formati da una coppia di caratteri di controllo, Ascii 13 e 10. Siccome utilizziamo le funzioni
    StringSegment ()
    e
    StringSegmentCount ()
    , nel ciclo
    For
    per sezionare il testo in righe, e dato che tali funzioni considerano un solo carattere come separatore degli eventuali frammenti, dobbiamo preventivamente rimuovere eventuali caratteri Ascii 13 presenti nel testo, in modo da far rimanere solo l’Ascii 10 come carattere di fine riga.
  3. "Il comando EnterKey () viene eseguito prima della scrittura."
    Nel ciclo
    For
    originale, dopo ogni scrittura di una riga veniva simulato il ritorno a capo tramite il comando
    EnterKey ()
    , perché noi sapevamo che l’unico testo da scrivere aveva questa caratteristica. Se però una funzione deve servire per scrivere qualsiasi testo, bisogna prevedere anche l’eventualità che una stringa non termini con un ritorno a capo, e che quindi il cursore debba fermarsi sulla stessa riga in cui si è iniziato a scrivere, appena dopo l’ultimo carattere trattato. Per fare questo, bisogna appunto prevedere che la simulazione del ritorno a capo avvenga prima di iniziare a scrivere una nuova riga, non dopo, e perciò solo a partire dal secondo passaggio del ciclo
    For.
  4. "Controllo se il testo termina con un carattere di fine riga."
    Per quanto detto al punto precedente, si effettua un controllo se l’ultimo carattere a destra della stringa sia un carattere Ascii 10, cioè se tale stringa termini a fine riga. Se sì, tale controllo aggiunge una unità ai ritorni a capo da conteggiare, altrimenti lascia il numero di ritorni così come è stato impostato tramite il parametro iniziale.
  5. "Ciclo suppletivo di aggiunta degli ulteriori ritorni a capo."
    Se come secondo parametro della funzione è stato impostato un valore positivo, e comunque se il precedente controllo sul carattere di Fine Riga ha aggiunto una unità al contatore, si esegue un ciclo che per ciascun ritorno a capo richiami il comando
    EnterKey ()
    ed aggiorni il contatore delle righe.
  6. "Controllo sul numero di righe per la lettura."
    Si subordina la pronuncia del numero di righe al fatto che ve ne sia almeno una con un ritorno a capo, altrimenti sarebbe sufficiente il comando di lettura riga corrente. Soprattutto, si effettua poi un controllo se le righe da scrivere sono una soltanto, oppure più d’una. Nel primo caso, sarebbe pronunciato in tono minore il testo immesso nel documento, mentre nel secondo sarà letto un messaggio formattato con il numero di righe inserite.

Esercizio 6.4.3. Convertire una copia di uno script nella funzione MetteTesto.

FileScript.

Default.JSS

Nome.

MetteTesto

Novità.
  1. La funzione integrata
    StringReplaceSubstrings
    , (SostituisciSottoStringaInStringaTestuale), la quale serve appunto per sostituire tutte le occorrenze di un testo in un altro. Nel dettaglio, essa ha tre parametri:
    • Il testo da trattare.
    • la stringa da trovare per sostituirla.
    • La stringa usata per la sostituzione. Nel caso in cui una stringa da cercare debba essere soltanto rimossa, si porrà come terzo parametro una stringa vuota.
  2. La costante
    CR
    , contenente il carattere di ritorno a capo, Ascii 13.
Note.
  1. Portatevi all’interno dello script
    AperturaCodice
    ,selezionatene il contenuto con
    Control+R
    , quindi copiatelo negli appunti.
  2. Andate alla fine del File, create lo spazio di una riga, quindi incollatelo.
  3. Poiché lo script duplicato è stato incollato nello stesso file script dove si trova ancora l’originale, per prima cosa andate sulla riga d’intestazione della copia appena incollata e modificate manualmente il nome dello script in
    MetteTesto
    . Tale passaggio è indispensabile, perché altrimenti, se si lasciasse originale e copia con lo stesso nome nello stesso file script, eventuali modifiche eseguite tramite il comando
    Visualizza Documentazione
    , porterebbero a perdere dati nello script originale.
  4. Una volta aggiornato il nome, premete
    Control+D
    per visualizzare la schermata di modifica. Qui, passate al secondo campo e disattivate il controllo
    Può essere assegnato al tasto
    , quindi continuate ad aggiornare i dati secondo le indicazioni poste di seguito.
Descrizione.

Scrive il testo specificato come parametro.

Ritorni.

Di tipo Void. Nessuno.

Parametri.
  1. sDati. Il testo da scrivere. Di tipo String.
  2. iRitorni. Il numero dei ritorni a capo da aggiungere. Di tipo Int.

Codice.


Void Function MetteTesto (string sDati, int iRitorni)
Var
Int i, ; contatore del ciclo
Int iRighe, ; numero delle righe copiate
String sRiga; singolo segmento da scrivere
SpeechOff (); spegne la sintesi
; rimuove dal testo eventuali caratteri di ritorno a capo, Ascii 13
Let sDati = StringReplaceSubstrings (sDati, CR, NULLO)
; esegue un ciclo, ripetendolo per il numero di segmenti nella stringa
For i = 1 To StringSegmentCount (sDati, LF)
If i > 1 Then; dal secondo passaggio in poi,
EnterKey (); simula la pressione di Invio, per creare una nuova riga
Let iRighe = iRighe + 1; aggiorna il contatore delle righe copiate
Pause (); sospende momentaneamente il flusso
EndIf; fine controllo passaggi
Let sRiga = StringSegment (sDati, LF, i); estrae il segmento equivalente ad una riga,
TypeString (sRiga); e lo trascrive nel documento
EndFor; fine ciclo scansione testo
If StringRight (sDati, 1) == LF Then; se l’ultimo carattere del testo è quello di nuova linea,
Let iRitorni = iRitorni + 1; aggiunge una unità ai ritorni a capo da creare
EndIf; fine controllo ultimo carattere
For i = 1 To iRitorni; aggiunge eventuali o ulteriori ritorni a capo
EnterKey (); crea un’ulteriore nuova riga
Let iRighe = iRighe + 1; aggiorna il contatore delle righe copiate
EndFor; fine ciclo ritorni a capo
SpeechOn (); riattiva la sintesi
If iRighe Then; se sono state copiate delle righe intere,
If iRighe > 1 Then; se le righe sono più di una,
Let sDati =FormatString (msgMettiRighe, iRighe); formatta il dato con il numero di righe
EndIf; fine controllo numero righe
SayMessage (OT_ERROR, sDati); legge il messaggio in tono minore
EndIf; fine controllo righe copiate
Refresh (); resetta la schermata
SayLine (); legge la riga corrente
EndFunction

Collaudo.

Se provate a compilare ed è tutto a posto, per il test sul funzionamento dovremo rinviarvi al termine dei prossimi due esercizi, dove ci limiteremo ad aggiornare gli script già creati.

Esercizio 6.4.4. La versione aggiornata di InserisceFirma ().

FileScript.

Default.JSS

Nome.

InserisceFirma

Novità.
  1. La nostra funzione
    MetteTesto ()
    , al suo primo utilizzo.
  2. La costante
    RIGA_VUOTA
    , che corrisponde al valore 2, e che ci consente di far scrivere una riga vuota dopo l’ultima del testo.
Note.
  1. L’obiettivo di questo aggiornamento sarà rimuovere tutto il precedente contenuto dello script, sostituendolo con la chiamata alla nostra citata funzione. Ad essa, sarà posto come primo parametro il testo in precedenza passato alla funzione nativa
    TypeString ()
    , mentre come secondo sarà inserita la citata costante.

Codice.


Script InserisceFirma ()
MetteTesto (INIZIO_PERSONALE, RIGA_VUOTA); invia l’etichetta Personale alla funzione
EndScript

Esercizio 6.4.5. La nuova versione di AperturaCodice ().

FileScript.

Default.JSS

Nome.

AperturaCodice

Note.
  1. La modifica dello script sarà del tutto analoga a quella appena eseguita, con l’unica differenza che il testo passato come primo parametro sarà in questo caso l’elenco delle dichiarazioni iniziali.

Codice.


Script AperturaCodice ()
MetteTesto (lstApreScript, RIGA_VUOTA); invia le dichiarazioni di apertura alla funzione
EndScript

Collaudo.

Nel caso di buon esito nella compilazione, finalmente possiamo provare la funzione convertita.

  1. Aprite il file script
    Wordpad.JSS
    , che sinora avevamo lasciato senza intestazioni personali, e portatevi sulla riga di intestazione del primo script da noi realizzato nell’applicativo.
  2. Provate quindi a premere dapprima
    InserisceFirma ()
    , con
    Shift+Control+1
    , ed attendete l’esito. Sentirete pronunciare prima il messaggio
    "2 righe inserite"
    , poi sarà letta l’intestazione dello script.
  3. Procedete quindi con l’altro script, premendo
    Shift+Control+2
    , e dovreste avere inserite nel file anche le nostre dichiarazioni iniziali per gli script.
  4. Poiché anche in questo caso si tratta di un file che aveva certamente la dichiarazione di inclusione del file costanti definito, decidete voi se è il caso di cancellare direttamente la riga appena inserita che prevede tale istruzione, oppure lasciatela al suo posto, grazie al carattere Punto e Virgola che ne impedisce l’esecuzione.
  5. In ogni caso, chiudete salvando il lavoro eseguito.

***

Il controllo sulla finestra attiva.

I due script che abbiamo appena provato sono di quella categoria che possiamo definire
pericolosi
, in quanto eseguono delle azioni concrete su un eventuale documento aperto senza curarsi di dove ci si trovi. In pratica, noi avremmo bisogno di un controllo da inserire in questi script, il quale risponda, con un TRUE o un FALSE, se siamo nella finestra da noi richiesta. Per il momento, però, non disponiamo ancora di tutti gli elementi necessari, e solo nel prossimo capitolo riusciremo a realizzare un tale controllo.

Nel frattempo, almeno per un particolare tipo di finestra, una possibilità di eseguire questo tipo di valutazione ce la siamo costruita, ed ora basta solo applicarla agli script.

Analizzate questa struttura di controllo:


If CodiceFinestra (CORRENTE) != CODICE_EDITOR Then; se la finestra non è corretta,
Beep (); emette un segnale acustico,
Return; e interrompe il flusso
EndIf; fine controllo finestra

Almeno dai commenti, dovreste aver compreso che, grazie alla nostra funzione
CodiceFinestra ()
, possiamo confrontare il codice identificativo personale della finestra corrente, quella da dove si sono premuti i tasti di attivazione, con la stringa che avevamo registrato nella costante
CODICE_EDITOR.

In particolare, il controllo determina che, se non si tratta della finestra corretta, il flusso dello script si debba interrompere, dopo aver emesso un segnale acustico.

Questo tipo di controllo dovrebbe agire in entrambi gli script, ma non è necessario che noi lo si inserisca nei due singoli codici. Poiché gli script richiamano la stessa funzione, infatti, possiamo più facilmente porre il controllo soltanto all’interno della funzione.

In questo caso, il controllo è posto in un solo elemento di codice anziché in due, ed il risparmio si riduce solo a qualche riga. Più avanti, invece, la soluzione di porre i controlli nelle funzioni anziché negli script sarà usata anche su vasta scala, consentendoci di risparmiare anche centinaia di righe di codice alla volta.

Lo script Trasparente.

Gli script, o in questo caso le funzioni, quando eseguono il corpo del codice in esso contenuto solo se ricorrono determinate condizioni, sarebbe opportuno che fossero
trasparenti
. Con questo termine si indica il fatto che, qualora la finestra non sia quella giusta dove far agire l’elemento di codice, la combinazione di tasti usata per attivarli dovrebbe essere restituita all’applicazione, ripetendo tale pressione come se i tasti non avessero nemmeno attivato l’elemento di codice stesso.

Nella forma che vi abbiamo appena proposto di inserire, invece, quando non ci si trovi in una finestra di editing, la pressione dei tasti di attivazione sarebbe annullata, limitandosi ad emettere un segnale sonoro.

Questo può andare bene per il tipo di combinazione come quella che abbiamo usato per i due script appena elaborati, ma potrebbe essere limitante quando ad essere usate sono scelte rapide più semplici, ad esempio quelle con il tasto
Control
seguito da una lettera. In questo caso, infatti, ad una tale combinazione potrebbe essere assegnato un diverso comando in una delle altre finestre della stessa applicazione.

La risposta più semplice a questa esigenza l’avevamo anche già analizzata nel terzo capitolo,
TypeCurrentScriptKey
, la funzione integrata che svolge proprio il compito di riproporre la pressione dei tasti con i quali è stato eseguito uno script. Las’petto positivo di tale istruzione è che ripristina gli ultimi tasti premuti anche nel caso, come il nostro, in cui lo script abbia poi chiamato a sua volta un’altra funzione.

Quindi, sul piano pratico, tornando alla forma di controllo che si propone di inserire nella nostra funzione
MetteTesto ()
, sarà sufficiente sostituire l’istruzione che emette il segnale acustico,
Beep ()
, con questa appena citata. Si potrebbe anche lasciare pure il segnale acustico ma, qualora la pressione ripetuta tramite
TypeCurrentScriptKey ()
avesse un effetto, si avrebbe un segnale d’errore assieme ad un’azione valida.

Il consiglio, quindi, è quello di inserire per il momento solo la funzione di ripetizione tasti, e lasciare all’applicazione l’onere di interpretare nella maniera più corretta la pressione avvenuta. Nel prossimo capitolo, in ogni caso, avremo modo di dare al problema sollevato una soluzione ancora più versatile ed efficace.

Esercizio 6.5.2. La nuova versione di MetteTesto ().

FileScript.

Default.JSS

Nome.

MetteTesto

Novità.
  1. La costante
    CODICE_EDITOR
    , ampiamente analizzata, ma giunta ora alla sua prima applicazione in uno script definitivo.
Note.
  1. Le informazioni fornite nella premessa dovrebbero essere sufficienti ad indicarvi cosa fare, pertanto, ci limitiamo a proporre di seguito la forma modificata.

Codice.


Void Function MetteTesto (string sDati, int iRitorni)
If CodiceFinestra (CORRENTE) != CODICE_EDITOR Then; se la finestra non è corretta,
TypeCurrentScriptKey (); ripristina i tasti premuti,
Return; e interrompe il flusso
EndIf; fine controllo finestra
Var
Int i, ; contatore del ciclo
Int iRighe, ; numero delle righe copiate
String sRiga; singolo segmento da scrivere
SpeechOff (); spegne la sintesi
; rimuove dal testo eventuali caratteri di ritorno a capo, Ascii 13
Let sDati = StringReplaceSubstrings (sDati, CR, NULLO)
; esegue un ciclo, ripetendolo per il numero di segmenti nella stringa
For i = 1 To StringSegmentCount (sDati, LF)
If i > 1 Then; dal secondo passaggio in poi,
EnterKey (); simula la pressione di Invio, per creare una nuova riga
Let iRighe = iRighe + 1; aggiorna il contatore delle righe copiate
Pause (); sospende momentaneamente il flusso
EndIf; fine controllo passaggi
Let sRiga = StringSegment (sDati, LF, i); estrae il segmento equivalente ad una riga,
TypeString (sRiga); e lo trascrive nel documento
EndFor; fine ciclo scansione testo
If StringRight (sDati, 1) == LF Then; se l’ultimo carattere del testo è quello di nuova linea,
Let iRitorni = iRitorni + 1; aggiunge una unità ai ritorni a capo da creare
EndIf; fine controllo ultimo carattere
For i = 1 To iRitorni; aggiunge eventuali o ulteriori ritorni a capo
EnterKey (); crea un’ulteriore nuova riga
Let iRighe = iRighe + 1; aggiorna il contatore delle righe copiate
EndFor; fine ciclo ritorni a capo
SpeechOn (); riattiva la sintesi
If iRighe Then; se sono state copiate delle righe intere,
If iRighe > 1 Then; se le righe sono più di una,
Let sDati =FormatString (msgMettiRighe, iRighe); formatta il dato con il numero di righe
EndIf; fine controllo numero righe
SayMessage (OT_ERROR, sDati); legge il messaggio in tono minore
EndIf; fine controllo righe copiate
Refresh (); resetta la schermata
SayLine (); legge la riga corrente
EndFunction

Collaudo.

  1. Se la compilazione ha buon esito, aprite un documento anche vuoto in un qualsiasi altro applicativo, ad esempio il Blocco Note, e premete i tasti di attivazione di uno dei due script di scrittura testo. In questo caso, non dovrebbe succedere nulla: se volete, come detto, è sempre possibile porre un segnale acustico che vi avverta dell’errore.
  2. Per fare anche la controprova, tornate all’Editor, e qui aprite il file script per Microsoft Word. Qui, portatevi sulla nostra etichetta Personale, e scendete sino alla prima riga di codice, che dovrebbe essere l’intestazione dello script
    TrovaTesto ()
  3. Provate a premere
    Shift+Control+2
    , per inserire le dichiarazioni per inizializzare il nostro codice, che qui non avevamo ancora eseguito. Se tutto va bene, dovrebbe regolarmente scriverle per poi rileggervi la riga sul cursore.
  4. Prima di compilare, poiché in questo caso nella parte di script originali per Microsoft Word l’inserimento di
    HJConst.JSH
    è sicuramente presente, decidete come al solito voi se cancellare la nostra istruzione che la prevede, oppure se lasciarci davanti il carattere Punto e Virgola, casomai ricompilando poi per salvare l’eventuale modifica.
  5. Ora, terminata la parte più ricca di contenuti, concludiamo il capitolo con un argomento meno impegnativo.

***

Emettere dei suoni con Jaws.

quello dell’emissione di Suoni, che Jaws usa spesso per integrare le informazioni testuali fornite, è uno di quei temi che sin qui non sono mai stati esaminati. Non si è trattato tuttavia di una dimenticanza, bensì di una scelta ben precisa, operata per una serie di motivi.

Il primo tra questi è che, banalmente, i suoni non sarebbero di per sé indispensabili nel nostro lavoro. In altre parole, che ci siano i suoni oppure no, gli script funzionano ugualmente, ma grazie a loro sarà possibile controllare più facilmente l’azione dei nostri elementi di codice, senza che siano necessari i messaggi. Oltre a ciò, aspetto non trascurabile, gli script da noi prodotti possono risultare in generale più accattivanti, e comunque più simili a qualunque altra applicazione per computer.

Sul piano pratico, tuttavia, negli esempi sin qui proposti la presenza dei suoni avrebbe rappresentato solo l’onere di altro codice da scrivere, e da riportare in queste pagine. Proprio per questo motivo, tale modalità sarà proposta assai poco anche da qui in avanti.

Come in altre occasioni, il nostro intento è quello di fornirvi per ciascun argomento le nozioni che ci paiono indispensabili. Poi starà ad ognuno di voi applicarsi alle informazioni fornite, adattando i propri script secondo i gusti e le preferenze personali.

Come al solito, proporremo anche degli esercizi con una procedura da realizzare, la quale prevede due funzioni ed uno script. Lo scopo sarà far eseguire a Jaws la prima parte di una famosa canzone per bambini,
"Fra Martino Campanaro"
, il cui brano sarà proposto in modo il più fedele possibile all’originale.

Nel dettaglio, per far emettere suoni a Jaws, esistono due funzioni integrate:

  1. Beep ()
    , che abbiamo già usato, la quale esegue un suono fisso per tonalità e durata, ed è pertanto priva di parametri.
  2. PlaySound
    , (EseguiSuoni), la quale fa ascoltare un normale file audio, con estensione
    WAV
    , il cui nome, completo di percorso e di estensione, si deve specificare come parametro. Quando non si indica un percorso assieme al nome del file, allora Jaws cerca se ve ne siano con quel nome nel percorso di ricerca dei file di sistema, quello registrato nella variabile d’ambiente
    Path.

In generale, i file che Jaws usa per produrre i suoni dei suoi script sono memorizzati in una delle cartelle riservate ai file condivisi. Per farvi un’idea del materiale a nostra disposizione, potendo quindi scorrere i file audio presenti, seguite questi passi:

  1. Scegliete il collegamento
    Impostazioni condivise
    , tra le
    Utility
    della vostra versione di Jaws.
  2. Selezionate e quindi aprite la cartella
    Sounds
    , che dovrete casomai ricercare dentro ad una chiamata
    Settings
    . Vi sarà proposto un elenco di circa duecento file con estensione
    WAV
    , ed un nome in inglese da cui decifrare il loro contenuto.
  3. Scorrendoli con le frecce, e cliccandovi per farli eseguire dal sistema operativo, potrete così associare il nome, che dovrà essere usato come parametro, al suono prodotto dal singolo file.

Anche tra le
Impostazioni personali
, raggiungibili con l’omonimo collegamento nelle
Utility
di Jaws, esiste una cartella
Sounds
, destinata a contenere i file audio che vorrete utilizzare nei vostri script. Proprio per questo, all’inizio la cartella sarà vuota, nell’attesa che voi vi aggiungiate dei file di suoni personalizzati, prodotti da voi stessi o presi da altre fonti.

Per individuare un file da eseguire, Jaws mette a disposizione un’altra funzione integrata,
FindJAWSSoundFile
, (CercaFileSuoniDiJaws). Questa funzione, come si deduce dalla traduzione, cerca un file audio, il cui nome è stato specificato come primo parametro, nella cartella di suoni Condivisa o in quella Personale. Se il file viene trovato, allora ne restituisce lo stesso nome, ma completato dal percorso, mentre se non lo trova restituirà una stringa vuota.

Se a tale funzione non si specifica alcun secondo parametro, o si assegna ad esso il valore FALSE, il file sarà cercato in entrambe le cartelle. In questo caso, se vi fossero in ciascuna di esse un file con lo stesso nome, sarà comunque eseguito quello dentro alla cartella dell’Utente. Infine, qualora nel secondo parametro fosse specificato il valore 1, oppure TRUE, il file sarà cercato soltanto nella cartella suoni presente nelle
Impostazioni personali.

Ecco, di seguito, un esempio di utilizzo della funzione
PlaySound ()
, espressa in abbinamento con
FindJAWSSoundFile ()
. Come esempio di nome di file, abbiamo usato un suono che corrisponde alla nota
Do
, posta come prima nella scala musicale di cui si dispongono i file audio, e presente nella cartella suoni condivisa:


PlaySound (FindJAWSSoundFile ("Piano1-C.WAV")); suona la prima nota Do

Come si può notare, la sintassi è abbastanza corposa, soprattutto per le due funzioni annidate, l’una dentro all’altra. Per semplificarne la forma potremmo servirci di nostre funzioni, al cui interno potremmo inoltre usare anche delle costanti, in grado di facilitare l’immissione di parametri.

Questo è l’altro motivo per il quale i suoni non sono stati proposti sinora: solo in questo capitolo, infatti, abbiamo imparato a costruirci le nostre funzioni, ed alla sua conclusione ne scriveremo un altro paio.

Inoltre, nei nomi di questo tipo di file, che contemplano otto note singole da un
Do
all’altro di un’ottava, a variare sono un numero, dall’uno all’otto, ed una lettera, che corrisponde alla classificazione anglosassone delle note musicali. Per questo, come altro metodo per semplificare la loro esecuzione, si possono sfruttare anche le parti comuni di alcuni nomi di file, come ad esempio quello relativo alla nota che abbiamo proposto nella precedente istruzione.

A tal fine, potremmo realizzare due semplici funzioni, le quali avranno i seguenti scopi:

  1. Restituire il nome di un file, presente nella cartella dei suoni condivisi, indicandone solamente il numero progressivo.
  2. Emettere materialmente i suoni, ponendo come parametri sia il nome di file, cui aggiungeremo la funzione che ne cerca il percorso completo, sia un tempo di ritardo tra un suono e l’altro, utile nel caso come il nostro in cui dobbiamo mettere assieme un motivetto.

Grazie a queste due funzioni, l’istruzione precedente, che emetteva una nota
Do
, si potrà esprimere con questa sintassi molto più intuitiva:


Suona (Nota (1), iTempo)

Esercizio 6.6.1. La funzione Nota.

FileScript.

Default.JSS

Nome.

Nota

Descrizione.

Esegue le note di un’ottava, indicando il loro numero progressivo.

Ritorni.

Di tipo Void. Nessuno.

Parametri.
  1. iValore. Il numero progressivo della nota da emettere. Di tipo Int.
Novità.
  1. La funzione integrata
    MakeCharacterFromValue
    , (CreaCarattereDalValore), che restituisce il carattere corrispondente al codice Ascii di cui si immette il valore come parametro. Ad esempio, specificando un valore 65, il carattere ottenuto sarà una
    A
    maiuscola.
  2. Le costanti
    INIZIO_C
    , che ha come valore 66, e
    INIZIO_A
    , che invece vale 59. Esse costituiscono i numeri base che andranno sommati al valore immesso come parametro, al fine di ottenere la lettera che cambia all’interno del nome del file, a seconda della nota che esso contiene.
Fasi.
  1. Una struttura di controllo determina se un valore è inferiore o uguale a 5, e così imposta un certo numero base. Se invece il numero indicato come parametro è superiore a tale valore, ne sarà impostato un altro.
  2. Viene restituito il nome formattato del file, inserendo sulla stringa base sia il valore specificato come parametro, sia la lettera ottenuta convertendo in carattere il Codice Ascii del valore stesso cui è stato aggiunto il numero impostato tramite la struttura di controllo.
Note.
  1. La creazione della lettera che varia nel nome del file audio è complicata dal fatto che essa non è lineare. Nel dettaglio, dalla prima nota
    Do
    che corrisponde al file che ha nel suo nome la lettera
    C
    , si va fino alla nota
    Sol
    che corrisponde alla lettera
    G
    , quindi si riparte con la successiva nota
    La
    che ha nel nome la lettera
    A
    , per finire nuovamente con il
    Do
    , questa volta dell’ottava superiore, che ha di nuovo una lettera
    C
    . Per questo, sulla base del valore indicato come parametro, si imposteranno due diverse cifre di partenza cui sarà sommato, come detto, il valore stesso.

Codice.


String Function Nota (int iValore)
Var Int iBase; aggiunta per conversione in lettera
If iValore <= 5 Then; se le note sono fino al Sol,
Let iBase = INIZIO_C; imposta una base per lettere dalla C alla G
Else; altrimenti, se sono note dal La al Do alto,
Let iBase = INIZIO_A; imposta una base per lettere dalla A alla C
EndIf; fine impostazione base
; restituisce il nome del file, opportunamente formattato
Return FormatString (lstFileNote, iValore, MakeCharacterFromValue (iBase + iValore))
EndFunction

Collaudo.

Come al solito, per questa e per la prossima funzione, si rinvia il test della procedura al successivo script.

Esercizio 6.6.2. La funzione Suona.

FileScript.

Default.JSS

Nome.

Suona

Descrizione.

Esegue il file audio indicato come primo parametro, sospendendo poi il flusso per un numero di decimi di secondo pari al valore specificato come secondo parametro.

Ritorni.

Di tipo Void. Nessuno.

Parametri.
  1. sFile. Il nome del file completo di percorso ed estensione. Di tipo String.
  2. iRitardo. Il numero di decimi di secondo per sospendere il flusso dopo l’esecuzione del file. Di tipo Int.
Novità.
  1. La funzione
    PlaySound ()
    , già descritta in precedenza.
  2. La funzione
    FindJAWSSoundFile ()
    , anch’essa già presentata.
Fasi.
  1. Il file di suoni specificato viene eseguito, annidando le due funzioni native.
  2. Il flusso viene sospeso per il numero specificato di decimi di secondo.

Codice.


Void Function Suona (string sFile, int iRitardo)
PlaySound (FindJAWSSoundFile (sFile)); suona il file di suoni specificato
Delay (iRitardo); sospende il flusso per i decimi di secondo specificati
EndFunction

Esercizio 6.6.3. Lo script FraMartino.

FileScript.

Default.JSS

Nome.

FraMartino

Sommario.

Riproduce un brano di Fra Martino Campanaro.

Descrizione.

Esegue in sequenza una serie di file audio, corrispondenti ciascuno ad una specifica nota, sospendendo poi il flusso per un determinato numero di decimi di secondo.

TastiAttivazione.

Shift+Control+Windows+M

Novità.
  1. Il primo utilizzo delle nostre funzioni
    Suona ()
    , e
    Nota ().
Note.
  1. Il codice si articola su quattro fasi principali, corrispondenti ciascuno ad un ciclo
    For
    da ripetere due volte.
  2. Ciascuna iterazione del ciclo esegue la sequenza di note che corrisponde ad una battuta del brano, casomai aggiungendo l’intervallo di tempo necessario a raggiungere il totale dei quattro quarti necessari per la stessa.

Codice.


Script FraMartino ()
Var
Int iTempo1, ; primo intervallo di tempo
Int iTempo2, ; secondo intervallo di tempo
Int i; contatore dei cicli
Let iTempo1 = 4; imposta il valore intero
Let iTempo2 = 2; imposta un tempo dimezzato
For i = 1 To 2; esegue le prime due battute
Suona (Nota (1), iTempo1)
Suona (Nota (2), iTempo1)
Suona (Nota (3), iTempo1)
Suona (Nota (1), iTempo1)
EndFor; fine prime due battute
For i = 1 To 2; esegue terza e quarta battuta
Suona (Nota (3), iTempo1)
Suona (Nota (4), iTempo1)
Suona (Nota (5), iTempo1)
Delay (iTempo1)
EndFor; fine terza e quarta battuta
For i = 1 To 2; esegue quinta e sesta battuta
Suona (Nota (5), iTempo2)
Suona (Nota (6), iTempo2)
Suona (Nota (5), iTempo2)
Suona (Nota (4), iTempo2)
Suona (Nota (3), iTempo2)
Delay (iTempo2)
Suona (Nota (1), iTempo2)
Delay (iTempo2)
EndFor; fine quinta e sesta battuta
For i = 1 To 2; esegue penultima ed ultima battuta
Suona (Nota (2), iTempo1)
Suona (Nota (5), iTempo1)
Suona (Nota (1), iTempo1)
Delay (iTempo1)
EndFor; fine penultima e ultima battuta
EndScript

Collaudo.

Se è tutto a posto, l’esecuzione dello script può essere effettuata da qualunque ambiente ci si trovi, facendo sentire le prime otto battute della celebre canzoncina per bambini. Ai musicisti più attenti si chiede venia per aver modificato la penultima nota delle ultime due battute, ponendola di un’ottava superiore al dovuto, causa l’assenza del suono corrispondente tra quelli forniti da Jaws.

***

Riepilogo.

Dopo quest’ultima divagazione dedicata ai Suoni, possiamo concludere che lo scopo principale del capitolo, quello di far controllare ai nostri script dove stiano operando, è in qualche modo riuscito.

In realtà, siamo ben lontani da quello che si potrebbe definire un successo: per riuscire nell’intento, siamo stati costretti a rilevare manualmente i dati che hanno poi formato il codice identificativo personale, il quale ci serviva per riconoscere la finestra di editing. Inoltre, sempre a mano, abbiamo dovuto assegnare il valore alla costante per il confronto con i dati correnti.

Se si pensa di applicare tale controllo su larga scala, che possa essere esteso potenzialmente a tutte le finestre dei diversi applicativi, è necessario trovare il modo di automatizzare queste funzioni.

E a questo sarà dedicato il prossimo capitolo, dove conosceremo come Jaws si serva di molti dati memorizzati in file testuali. Sarà inoltre l’occasione per creare ulteriori nostre funzioni e procedure, al fine di automatizzare al massimo il controllo delle finestre.

Download

File script da scaricare, per gli utenti di Jaws fino alla versione 18
Archivio da scaricare, per gli utenti dalla versione 2018 in poi

Per ulteriori spiegazioni, scrivere a:

Abramo Volpato

Lascia un commento

Il tuo indirizzo email non sarà pubblicato. I campi obbligatori sono contrassegnati *