Gli script di Jaws 08. Funzioni Evento e Oggetti.

Programmiamoli da Soli!

Verso il traguardo.

Dopo la grande produzione di codice nel capitolo precedente, la tentazione sarebbe quella di mettere subito a frutto il lavoro svolto, considerato che i controlli messi a punto ci consentirebbero, oltre ad operare d’ora in poi con maggiore sicurezza, anche di aggiornare gran parte degli script sinora realizzati.

Nella nostra ipotetica
"cassetta degli attrezzi"
, tuttavia, mancano ancora alcuni temi da conoscere e sperimentare, di cui ci occuperemo in questo capitolo.

Partiremo dalle
Funzioni Evento
, il terzo tipo di questi elementi di codice, di cui ci limiteremo tuttavia a tracciare i termini generali. Iniziando con le esercitazioni di esempio che produrremo sull’argomento, getteremo le basi per una funzionalità interessante che sarà sviluppata nella sua forma definitiva solo più avanti.

Nella seconda parte di questo capitolo, quindi, parleremo dei file compilati, con estensione JSB, e di come poterli inserire nei nostri file sorgenti in formato testuale, per aumentare il codice da noi elaborato mantenendo però un certo ordine. Così facendo, potremo inoltre integrare nei nostri script anche dei file compilati di cui non abbiamo a disposizione il codice sorgente, non solo conservando tutte le funzionalità già presenti, ma in più aggiungendovi anche le nostre.

Al termine, come ultimo argomento, ci limiteremo ad illustrare cosa siano gli
Oggetti
, gli altri elementi oltre alle finestre che possiamo interrogare per ricavarne informazioni utili alle nostre esigenze, e di come li si può trattare con Jaws.

Prima di iniziare la parte informativa, apriamo la solita parentesi nella quale ci dedichiamo ai nostri file personali.

Esercizio 8.1.1. Aggiornare il file Personale delle Costanti.


CONTROL_G = "Control+G", ; combinazione tasti omologa
RIGA = "Riga", ; termine omonimo
SOSTITUISCI = "Sostituisci", ; termine omonimo
TASTO_JAWS = "JAWSKey", ; nome inglese del Tasto JAWS
INSERT = "Insert", ; Tasto omonimo
AUTOMATICA = "Automatica", ; termine omonimo
DIRETTA = "Diretta", ; termine omonimo
CODICE = "Codice", ; termine omonimo
PRIMO = "Primo", ; termine omonimo
SUCCESSIVO = "Successivo", ; termine omonimo
ALT_G = "Alt+G", ; combinazione tasti omologa
ALT_S = "Alt+S", ; combinazione tasti omologa
PULSANTE_RADIO = 19, ; codice dei controlli pulsanti radio
PRECEDENTE = "Precedente", ; termine omonimo
UTENTE = 6, ; file delle Impostazioni personali
PUNTO = ".", ; carattere Punto

Esercizio 8.1.2. Aggiornare il file Personale delle Variabili Globali.


Int gnAnnullaRitorno, ; impedisce il ritorno all’ultima riga memorizzata
Int gnUltima; ultima posizione memorizzata

Esercizio 8.1.3. Aggiornare il file Personale dei Messaggi.


; Avvio di un’applicazione
@msgAvvioApp
Sto entrando in %1.
@@
; messaggio per uscita da un applicativo
@msgUscitaApp
Sono uscito da %1
@@
; attivazione di una nuova finestra
@msgNuovaFinestra
NuovaFinestra attivata
@@
; spostamento alla fine del documento aperto
@hlpFineFile
Fine del documento.
@@
; spostamento alla fine del documento aperto - versione corta
@hlpFineFile_corto
Fine.
@@
; Base per due termini affiancati
@msg2
%1%2
@@
; formatta il secondo termine
@msgSecondo
%2
@@
; Mancato ritrovamento del testo
@msgNoTesto
Stringa %1 non trovata.
@@
; base per due termini separati dal carattere Pipe
@msg2Pipe
%1|%2
@@
; elenco sezioni per i file Tasti
@lstSezioniTasti
Common Keys|Desktop Keys|Laptop Keys
@@
; titolo delle informazioni sui tasti di attivazione per l’applicativo corrente
@hlpInfoApp
Tasti Personali dentro a %1.
@@
; titolo delle informazioni sui tasti di attivazione nel file Predefinito
@hlpInfoJaws
Tasti di utilizzo generale.
@@
; termine inserito tra righe vuote
@msgTraVuote
%8
%1
%9
@@
; voce per le informazioni sui tasti di attivazione degli script personali attivi
@hlpInfoScript
%1 %2.
@@
; base per comando nell’Aiuto in Linea
@hlpInfoLink
script %1 ()
@@
; base per ultima voce della schermata d’aiuto
@hlpUltimaInfo
Visualizza questa schermata d’Aiuto. %1.
%9
%2
@@

***

Le funzioni Evento.

Questi elementi di codice sono il vero cuore pulsante del programma di sintesi vocale, in quanto regolano tutto ciò che succede automaticamente al verificarsi dei vari eventi nel PC. Ricordiamo che le funzioni Evento sono state tutte scritte dai programmatori di Jaws, e non se ne possono creare di nuove. L’unica cosa che si può fare è inserirne una copia laddove non ce ne siano, come ad esempio nei file script delle applicazioni, oppure aggiungere delle proprie istruzioni all’interno di quelle esistenti.

Esse terminano tutte con il suffisso
Event
al termine del nome, prima delle parentesi, quindi sono facilmente riconoscibili tra tutte le altre funzioni integrate di Jaws. In generale, il loro funzionamento è semplice:

  1. Nel PC succede un evento specifico, cui ciascuna di esse è programmata a recepire, ed esse si attivano da sole.
  2. Un codice che fosse scritto al loro interno viene così eseguito, ripetendosi ogni volta che si verifica l’evento.
  3. Se la funzione Evento avesse dei parametri, e noi glieli specificassimo, all’interno del codice sarà possibile usare questi valori memorizzati in variabili.

Lasciando ad un prossimo titolo le spiegazioni in dettaglio sull’uso dei parametri con gli Eventi, ci dedichiamo subito ad un’esperienza pratica nell’uso generale di tali funzioni.

Realizzare una procedura con le funzioni Evento.

Le
Funzioni Evento
rivestono una grande importanza nel lavoro di Jaws, ed hanno una gestione del tutto particolare. Proprio per questo, conviene per un po’ lasciare il file script Predefinito, limitandoci ad operare sui file delle singole applicazioni .

In particolare, ci serviremo per il momento solo di un file script che conosciamo ormai bene, quello del Blocco Note di Windows, Notepad.JSS. Nel dettaglio, inizieremo col realizzare una nostra versione di tre diverse funzioni Evento:

  1. AutoStartEvent
    , (EventoAutomaticoAllaPartenza), che si attiva ogni volta che si entra in un programma, o vi si rientra dopo averne aperto altri.
  2. AutoFinishEvent
    , (EventoAutomaticoAll’Uscita), che si attiva invece quando si esce dal programma in cui è scritta la funzione.
  3. WindowActivatedEvent
    , (EventoDiFinestraAttivata), che si verifica ogni volta che compare una nuova finestra nel programma cui appartiene lo script.

Di queste funzioni, ne realizzeremo dapprima delle versioni davvero minimali, il cui unico scopo sarà quello di farci sapere quando sono chiamate ad attivarsi.

In particolare, di queste tre funzioni appena elencate non useremo né ritorni né parametri. In realtà una di queste,
WindowActivatedEvent ()
, un parametro ce l’avrebbe, l’Handle della finestra che attiva la funzione, ma di questo aspetto ne parleremo solo più avanti.

Per quanto riguarda il modo di inserirle materialmente nel codice, è possibile utilizzare la procedura di creazione classica, avendo cura soltanto di rispettare il nome della funzione Evento, che come detto è già stabilito, e rispettare casomai il tipo dei parametri, anch’esso predefinito dai programmatori di Jaws.

In ogni caso, se si decide di copiare ed inserire nel codice una funzione Evento, sarà possibile compilarne il codice così come lo si incolla. Quando Jaws rileva in un file script una nuova funzione, infatti, assegna ad essa in modo automatico il tipo di ritorno
Void
,proprio quello che andrebbe assegnato a tali speciali funzioni.

In ogni caso, di ciascuna proporremo lo stesso alcuni cenni sul loro funzionamento, suddividendole nel classico sistema a schede. Unico sarà invece il collaudo, al termine del quale continueremo con altre pillole teoriche.

Esercizio 8.2.2. La funzione AutoStartEvent () per il Blocco Note.

FileScript.

Notepad.JSS

Nome.

AutoStartEvent

Descrizione.

Leggere un messaggio quando si entra nel Blocco Note.

Ritorni.

Di tipo Void. Nessuno.

Codice.


Void Function AutoStartEvent ()
; legge e formatta il messaggio con il nome dell’applicativo
SayFormattedMessage (OT_ERROR, msgAvvioApp, NULLO, GetActiveConfiguration ())
EndFunction

Esercizio 8.2.3. La funzione AutoFinishEvent () per il Blocco Note.

FileScript.

Notepad.JSS

Nome.

AutoFinishEvent

Descrizione.

legge un messaggio quando si esce da Blocco Note.

Ritorni.

Di tipo Void. Nessuno.

Codice.


Void Function AutoFinishEvent ()
; legge e formatta il messaggio con il nome dell’applicativo
SayFormattedMessage (OT_ERROR, msgUscitaApp, NULLO, GetActiveConfiguration ())
EndFunction

Esercizio 8.2.4. La funzione WindowActivatedEvent () per il Blocco Note.

FileScript.

Notepad.JSS

Nome.

WindowActivatedEvent

Descrizione.

Legge un messaggio quando si attiva una nuova finestra, enfatizzando la voce.

Ritorni.

Di tipo Void. Nessuno.

Novità.
  1. Il Tipo di Output con valore 28, registrato nella costante
    OT_CHAR
    , immesso nella funzione
    SayFormattedMessage ()
    . Questo tipo di Output enfatizza la voce, tramite l’aumento di tono, quando incontra una parola che contiene almeno una maiuscola. Questo valore sarebbe riservato all’uso interno, e quindi non è soggetto alle impostazioni degli Elementi di Prolissità. Nel nostro caso, sarà usato per far leggere a Jaws in modo diverso dagli altri due il messaggio che evidenzia l’attivazione di una nuova finestra.

Codice.


Void Function WindowActivatedEvent ()
; legge il messaggio enfatizzando le parole con almeno una maiuscola
SayMessage (OT_CHAR, msgNuovaFinestra)
EndFunction

Collaudo.

Dopo che il salvataggio e la successiva compilazione avrà avuto un buon esito, aprite un file anche vuoto nel Blocco Note, e seguite questi passi:

  1. Appena Entrati, una voce di Jaws vi segnalerà l’ingresso nel programma, il che significa che si è attivata la funzione
    AutoStartEvent ().
  2. Premete quindi la combinazione
    Alt+F
    per far comparire il menu File.
  3. Scorrete verso il basso fino a selezionare
    Apri
    , quindi premete Invio. Adesso una voce enfatizzata vi avvertirà che si è presentata una nuova finestra. Questo significa che è entrata in funzione
    WindowActivatedEvent ()
    . Tenete presente che questo è accaduto solo dopo che avete attivato il comando, mentre alla sola comparsa dei menu la finestra era rimasta la stessa.
  4. Uscite dalla schermata di apertura file, e ancora sentirete l’avviso della nuova finestra. Ora provate a passare ad un altro programma, premendo
    Alt+Tab
    . Sentirete dapprima ancora il cambio della finestra, poi sarete avvisati dell’uscita dal programma. In questo caso, sarà entrata in funzione dapprima
    WindowActivatedEvent ()
    , quindi
    AutoFinishEvent ().
  5. Ora, premete Alt+Tab fino a rientrare dentro al Blocco Note. Questa volta sentirete solo l’avviso dell’avvio del Blocco Note, e non la nuova finestra, perché questa si attiva solo quando si è già dentro al programma, non quando si arriva da fuori. In questo caso ad attivarsi è stata perciò solo
    AutoStartEvent ().
  6. Quando avete concluso tutte le prove, tornate al file
    Notepad.JSS
    dentro all’Editor di Script, cancellate il codice contenuto in ciascuna delle funzioni Evento, da
    AutoStartEvent ()
    a
    WindowActivatedEvent ()
    , lasciando così solo le intestazioni e l’istruzione di chiusura.
  7. Salvate compilando, e adesso potete tornare al Blocco Note per controllare che non vi sia più nessun messaggio strano. Terminata tale verifica, tenete aperto il Blocco note, che fra poco ci servirà ancora, e tornate invece all’Editor di Script, dove faremo la seconda parte dell’esercitazione.

***

Il ritorno alla riga registrata.

Lo spunto per questa parte dell’esercitazione ci è fornito da un’anomalia, che si riscontra proprio del Blocco Note che abbiamo appena usato. In questo programma, infatti, quando si conferma una sostituzione di testo, all’uscita dalla finestra di dialogo
Sostituisci
si tornerà comunque alla prima riga del file, a prescindere da dove si era posizionati prima di attivare il comando. Se ad esempio si fosse posizionati anche alla riga 1000 o 5000 di un file, prima di sostituire del testo, dopo aver eseguito il comando di sostituzione si ritornerà sempre alla riga numero 1.

Per risolvere questo problema, e non solo, dovremo sia aggiornare con del nuovo codice le tre funzioni Evento sin qui utilizzate, sia inserirne una nuova,
ScreenStabilizedEvent ()
, (EventoSchermoStabilizzato), che si verifica quando si concludono le elaborazioni in corso, ed il cursore si ferma nella finestra in quel momento attiva.

In ogni caso, tramite l’uso di tali funzioni Evento, gli obiettivi da raggiungere sono i seguenti:

  1. All’uscita dalla finestra di dialogo di sostituzione, far ritornare il cursore alla riga della finestra principale dove era posizionato in precedenza.
  2. Dato che il procedimento è lo stesso, faremo sì che si ritorni all’ultima riga registrata anche nel caso in cui si rientri nel file dopo esserne usciti.

Per gestire il ritorno materiale del cursore alla riga, dovremo realizzare delle nostre funzioni suppletive, che saranno comunque chiamate dalle funzioni Evento. In questi elementi di codice inseriremo, ovviamente, anche la nostra funzione
SiamoNellaFinestra ()
, per controllare di volta in volta che la finestra dove operare sia quella corretta.

In particolare, le possibili fasi della procedura potrebbero essere le seguenti:

  1. Da dentro alla finestra di editazione del Blocco Note, quella principale, quando il cursore cambia la riga dove è posizionato, sarà aggiornato un valore che ne indicherà il numero di riga, registrandolo in una variabile globale. Tale compito è svolto dalla funzione
    ScreenStabilizedEvent ().
  2. Quando si entra nella finestra di dialogo
    Sostituisci
    , ad opera di
    WindowActivatedEvent ()
    , oppure quando si esce dal Blocco Note, a cura di
    AutoFinishEvent ()
    , sarà azzerata la variabile globale che impedisce il ritorno all’ultima riga, di fatto consentendolo.
  3. Quando si ritorna alla finestra principale del Blocco Note, se il divieto impostabile tramite una variabile globale è disattivato, si ritornerà alla riga registrata. Nel caso in cui si esca dalla finestra di dialogo Sostituisci, sarà ancora
    WindowActivatedEvent ()
    ad entrare in funzione. Se invece si tornerà al file dopo esserne usciti, sarà
    AutoStartEvent ()
    ad intervenire.
  4. L’azione di ritorno alla riga sarà gestita tramite le nostre ulteriori funzioni.

La prima di queste, chiamata PosizioneCursore, restituirà appunto il numero di riga corrente. La seconda, che avrà nome
ValoreLunghezza
, fornirà in questo caso il numero complessivo delle righe nel documento.

Entrambi questi due elementi di codice saranno chiamati a loro volta dalla terza funzione, che avrà per nome
MuoveAllaRiga
, la quale sfrutta l’omologo comando di Blocco Note,
Vai a
, che si attiva con la scelta rapida
Control+G.

La quarta ed ultima nostra funzione, infine, eseguirà dei controlli sul se, e sul come, attivare la seconda, e si chiama, anche qui in modo molto esplicativo,
ControllaRitorno.

Ora, fatta una panoramica sui dettagli della procedura, passiamo a realizzare gli elementi di codice. Poiché, come detto, in tre delle funzioni Evento vi è solo da aggiornarne il contenuto, di queste faremo ciascuna una mini-scheda con le sole modifiche apportate e la nuova versione del codice.

Prima di aggiornare le versioni precedenti, tuttavia, consigliamo di inserire le cinque funzioni nuove, le nostre quattro realizzate da zero, e l’ulteriore funzione Evento
ScreenStabilizedEvent
. Per queste, come facciamo di solito, proporremo di seguito la scheda dettagliata.

Esercizio 8.3.1. La funzione PosizioneCursore.

FileScript.

Notepad.JSS

Nome.

PosizioneCursore

Descrizione.

Restituisce il valore numerico corrispondente alla riga del documento in cui è posto il cursore.

Ritorni.

Di tipo Int. Il numero della riga corrente.

Note.

In questa funzione l’unica istruzione sarà l’utilizzo della funzione nativa
GetCursorRow ()
, che abbiamo già incontrato. Il fatto di inserirla in un nostro elemento di codice che, in pratica, svolge la stessa mansione della funzione originale, sta nel fatto che si vuole creare un nome di funzione la quale più avanti sarà chiamata anche da script o funzioni provenienti dal file script Predefinito. Tale nostro elemento, nel caso del Blocco Note, coinvolge una funzione già esistente, ma in altri applicativi potrebbe avere contenuti diversi e specifici per le varie situazioni.

Codice.


Int Function PosizioneCursore ()
Return GetCursorRow (); restituisce la riga su cui è posto il cursore
EndFunction

Esercizio 8.3.2. La funzione ValoreLunghezza.

FileScript.

Notepad.JSS

Nome.

ValoreLunghezza

Descrizione.

Restituisce il numero di righe totali del documento.

Ritorni.

Di tipo Int. Il numero totale delle righe nel documento.

Novità.
  1. La funzione integrata
    GetLineCount
    , (OttieniConteggioRighe), che restituisce il numero delle righe totali rilevate nel documento. Senza parametri.
Note.

Il valore restituito dalla funzione nativa, all’interno del Blocco Note, differisce di una unità rispetto a quello che viene visualizzato sulla riga di stato, quella per intenderci che si trova in fondo allo schermo. Per questo, nella nostra funzione è previsto di aggiungere tale unità mancante al risultato della funzione nativa, proprio per correggere l’imprecisione rilevata.

Codice.


Int Function ValoreLunghezza ()
Return GetLineCount () +1; restituisce il numero totale delle righe, aggiungendone un’unità
EndFunction

Esercizio 8.3.3. La funzione MuoveAllaRiga.

FileScript.

Notepad.JSS

Nome.

MuoveAllaRiga

Descrizione.

Spostare il cursore all’ultima riga memorizzata, attivando il relativo comando del programma.

Ritorni.

Di tipo Int. L’esito dello spostamento: TRUE per la riuscita, FALSE per nessuno.

Novità.
  1. La nostra funzione
    PosizioneCursore ().
  2. La nostra funzione
    ValoreLunghezza ().
  3. La funzione integrata
    JAWSBottomOfFile
    , (FineFileDiJaws), che sposta il cursore alla fine del documento aperto. Senza parametri.
  4. La costante
    CONTROL_G
    , corrispondente all’omologa scelta rapida, che richiama il comando originale dell’applicativo sullo spostamento alla riga.
Fasi.
  1. Un primo doppio controllo si assicura che un numero di riga sia stato specificato, e che questo non sia lo stesso della riga corrente, ed in caso contrario interrompe il flusso restituendo un risultato nullo.
  2. Un secondo doppio controllo verifica intanto che il numero delle righe complessive del documento sia stato rilevato, quindi che il valore cui portarsi non sia uguale o superiore a quello delle righe totali.
  3. In caso positivo, richiama il comando del programma per lo spostamento riga, e assegna alla variabile globale il valore memorizzato.
  4. Nel caso in cui, invece, la riga cui portare il cursore non sia inferiore, si attiva il comando di Fine File, e viene letto l’avviso di tale opzione.

Codice.


Int Function MuoveAllaRiga (int iValore)
Var Int iTotale; lunghezza complessiva del documento
If !iValore ; se non è stato specificato alcun numero di riga,
|| iValore == PosizioneCursore () Then; oppure se la riga richiesta è la stessa dell’attuale,
Return FALSE; restituisce un risultato nullo
EndIf; fine controllo validità valore
Let iTotale = ValoreLunghezza (); rileva il valore complessivo
If !iTotale ; se la lunghezza del file non è stata rilevata,
||iValore < iTotale Then; oppure se il valore è inferiore alla lunghezza del documento,
SpeechOff (); spegne la sintesi
TypeKey (CONTROL_G); richiama il comando di spostamento alla riga
Pause ()
TypeString (IntToString (iValore)); immette la riga a cui portarsi
EnterKey (); simula la pressione di Invio
Pause ()
SpeechOn (); riattiva la sintesi
Return TRUE; restituisce l’esito positivo
Else; se invece il numero di riga è maggiore o uguale a quello delle righe totali,
JAWSBottomOfFile (); si porta alla fine del documento,
SayMessage (OT_ERROR, hlpFineFile, hlpFineFile_corto); avvisa dello spostamento,
Return FALSE; e restituisce un risultato nullo
EndIf; fine controllo righe totali
EndFunction

Esercizio 8.3.4. La funzione ControllaRitorno.

FileScript.

Notepad.JSS

Nome.

ControllaRitorno

Descrizione.

Gestisce l’eventuale ritorno all’ultima posizione registrata.

Ritorni.

Di tipo Void. Nessuno.

Novità.
  1. La variabile globale
    gnAnnullaRitorno
    , che indica lo stato del divieto al ritorno all’ultima riga registrata: se questa ha un valore positivo, il ritorno sarà impedito, se avrà invece un valore nullo, tale ritorno sarà invece consentito.
  2. La prima chiamata della nostra funzione
    MuoveAllaRiga ()
    , per far spostare il cursore alla riga indicata.
  3. La variabile globale
    gnUltima
    , dove sarà registrato il numero dell’ultima riga sulla quale era posizionato il cursore, da una prossima funzione Evento.
  4. La costante
    RIGA
    , equivalente al termine omonimo, che viene usata per formattare tale termine nel messaggio.
Fasi.
  1. Un primo controllo verifica che il divieto al ritorno alla riga sia disattivato, altrimenti s’interrompe il flusso.
  2. Una seconda struttura di controllo si accerta che il movimento all’ultima riga registrata sia riuscito, ed in tal caso legge il numero di riga con il tono minore della sintesi.
  3. In ogni caso, legge la riga corrente ed attiva il divieto al ritorno.

Codice.


Void Function ControllaRitorno ()
If gnAnnullaRitorno Then; se il divieto al ritorno all’ultima posizione è attivo,
Return; interrompe il flusso
EndIf; Fine controllo divieto
If MuoveAllaRiga (gnUltima) Then; se lo spostamento riesce,
; legge il numero di riga cui ci si è portati
SayFormattedMessage (OT_ERROR, msg2, msgSecondo, RIGA, gnUltima)
EndIf; fine controllo spostamento
SayLine (); legge la riga corrente
Let gnAnnullaRitorno = TRUE; attiva il divieto al ritorno all’ultima riga
EndFunction

Esercizio 8.3.5. La funzione ScreenStabilizedEvent () per il Blocco Note.

FileScript.

Notepad.JSS

Nome.

ScreenStabilizedEvent

Descrizione.

Imposta in una variabile globale il numero dell’ultima riga in cui si è posizionato il cursore.

Ritorni.

Di tipo Void. Nessuno.

Fasi.
  1. Un nostro controllo sulla finestra verifica se quella corrente è registrata come del tipo
    Editing
    , ed in tal caso lascia proseguire il flusso.
  2. Un secondo controllo verifica se la riga corrente sia diversa da quella memorizzata come ultima riga.
  3. Se i due controlli hanno esito positivo, aggiorna il valore memorizzato.

Codice.


Void Function ScreenStabilizedEvent ()
If SiamoNellaFinestra (EDITING) Then; se si è nella finestra corretta,
If PosizioneCursore () != gnUltima Then; se la riga attuale non è quella memorizzata,
Let gnUltima = PosizioneCursore (); aggiorna il valore
EndIf; fine controllo riga
EndIf; fine controllo finestra
EndFunction

Esercizio 8.3.6. La seconda versione di AutoStartEvent ().

FileScript.

Notepad.JSS

Nome.

AutoStartEvent

Descrizione.

All’ingresso nel programma, controlla se far ritornare o meno il cursore all’ultima riga registrata.

Novità.
  1. La prima chiamata della nostra funzione
    ControllaRitorno ()
    , che esegue materialmente i controlli e l’eventuale ritorno all’ultima riga.

Codice.


Void Function AutoStartEvent ()
If SiamoNellaFinestra (EDITING) Then; se si è nella finestra corretta,
ControllaRitorno (); verifica il possibile ritorno all’ultima riga registrata
EndIf; fine controllo finestra
EndFunction

Esercizio 8.3.7. La seconda versione di AutoFinishEvent ().

FileScript.

Notepad.JSS

Nome.

AutoFinishEvent

Descrizione.

All’uscita dal programma, azzera la variabile globale con il divieto del ritorno all’ultima riga, consentendo quindi di effettuare tale ritorno all’ingresso successivo nel Blocco Note.

Codice.


Void Function AutoFinishEvent ()
; consente un successivo ritorno all’ultima riga, disattivandone il divieto
Let gnAnnullaRitorno = FALSE
EndFunction

Esercizio 8.3.8. La seconda versione di WindowActivatedEvent ().

FileScript.

Notepad.JSS

Nome.

WindowActivatedEvent

Descrizione.

Nella finestra Editing, torna all’ultima riga registrata, mentre nella finestra Sostituisci, annulla il divieto a tale ritorno.

Novità.
  1. La costante
    SOSTITUISCI
    , che equivale all’omonimo nome del tipo di finestra da controllare.
Note.
  1. Nel codice, l’istruzione con
    Delay ()
    serve a ritardare il rilevamento della finestra quel tanto che basta per far sì che la finestra controllata sia effettivamente quella attiva in quel momento. Il valore assegnato un paio di capitoli fa alla costante
    ATTESA
    ,il numero 2, potrebbe però non essere sufficiente, in quanto diverso da PC a PC, sulla base della velocità del processore. Nel caso in cui la procedura di ritorno all’ultima riga avesse dei malfunzionamenti, provate innanzi tutto ad aumentare questo valore di una o due unità, che corrispondono ad altrettanti decimi di secondo, aprendo il file delle costanti
    _PsConst.JSH
    , modificando il valore nella relativa assegnazione, per poi salvare il file e tornare all’Editor di Script per ricompilare.
  2. Nel codice sottostante sono stati inseriti ben due dei nostri controlli sulle finestre ma, in entrambi i casi, non si è usato il secondo parametro, quello che attiva la fase Aiuto. Tale accorgimento serve ad impedire che questo tipo di controlli abbiano degli effetti indesiderati, poiché le funzioni Evento si attivano spesso ed in finestre di vario tipo.

Codice.


Void Function WindowActivatedEvent ()
Delay (ATTESA); ritarda il flusso per i decimi di secondo impostati
If SiamoNellaFinestra (EDITING) Then; se si è nella finestra primaria,
ControllaRitorno (); verifica il possibile ritorno all’ultima riga registrata
ElIf SiamoNellaFinestra (SOSTITUISCI) Then; se si è in quella di sostituzione,
Let gnAnnullaRitorno = FALSE; consente di tornare all’ultima riga, disattivandone il divieto
EndIf; fine controllo finestra
EndFunction

Le impostazioni preliminari ai controlli sulle finestre.

Se tutto è andato bene nell’Editor di Script, potete anche subito aprire un documento nel Blocco Note, badando che questo abbia almeno qualche riga di testo.

Come avete potuto notare, nella procedura abbiamo inserito, tra gli altri, anche dei controlli delle finestre. Per poter utilizzare tale sistema, dovremmo aver stabilito, e registrato nei file di configurazione, due dati fondamentali:

  1. Il nome che noi abbiamo assegnato ad un tipo di finestra da controllare, come per esempio
    Editing
    o
    Sostituisci
    , ed il livello a cui dobbiamo leggere i dati delle finestre, scegliendo tra
    "Corrente",
    "Reale"
    e
    "Principale".
  2. Un codice che identifica la finestra che corrisponde al nome che le abbiamo assegnato, salvato dalla procedura nel file di configurazione della singola applicazione.

Parlando del primo tipo di dati, Questi devono essere registrati nel nostro file di configurazione Personale.

A proposito della finestra
Editing
, il nome stesso ed il livello di lettura dei dati erano stati i primi da noi registrati. Per quanto riguarda quella che nel codice abbiamo identificato come
Sostituisci
, invece, i dati iniziali li dovremo salvare direttamente durante la fase di collaudo che stiamo affrontando, così come avevamo fatto nello scorso capitolo per il tipo denominato
Manager.

Nel dettaglio, una volta entrati nel Blocco Note, seguite questi passi.

  1. Premete la scelta rapida
    Control+H
    per aprire la finestra di dialogo
    Sostituisci
    . Da lì eseguite il nostro script per l’impostazione, con
    Control+Windows+I
    , così come un messaggio pronunciato da Jaws dovrebbe invitarvi a fare.
  2. Scegliete
    Immetti nuovo tipo
    , quindi inserite
    Sostituisci
    , quindi
    Reale
    come Livello, quindi confermate per registrare i dati della finestra.
  3. Una volta conclusasi la procedura di impostazione, con la seconda conferma dei dati salvati, si potrà premere Escape per tornare alla finestra principale del Blocco Note.

Se non avete avuto problemi, potete proseguire. Altrimenti, l’importante è analizzare sempre ciò che vi viene chiesto dalla procedura, rispondendo in modo corretto. Danni non se ne possono fare, e qualsiasi situazione non è irreversibile.

La parte più soggetta a sorprese è quella dei dati registrati per l’applicazione, e male che vada, seguite questi passi:

  1. Aprite la cartella dei file personali, con
    Control+O.
  2. Selezionando
    Tutti i file (*.*)
    , come tipo di file, cercate
    Notepad.JCF
    , e cancellatelo.
  3. Tornate nel Blocco Note, ripetendo i passi proposti nell’elenco precedente.

Per finire, un’importante annotazione: nelle finestre di livello inferiore a quella d’ingresso, come la
Sostituisci
appena configurata, ma anche le schermate
Trova,
Stampa
, eccetera, il livello da leggere per identificarle è quello
Reale
, perché quello
Corrente
restituirebbe i dati del singolo controllo su cui è posizionato il cursore all’ingresso.

La prova della nostra procedura per il ritorno alla riga registrata.

Ricordiamo che il nostro obiettivo era, innanzi tutto, far ritornare il cursore nell’ultimo punto memorizzato, sia quando avevamo operato una sostituzione nel documento, sia al nostro ritorno del Blocco Note dopo esserne usciti.

Per verificare se tutto funziona, seguite fedelmente questi passi:

  1. Dentro al Blocco Note, con un documento non vuoto, scendete di qualche riga nel testo, controllando a che numero siete arrivati anche tramite il nostro script apposito che si attiva con
    Control+J
  2. Tornate a premere la scelta rapida
    Control+H
    per la finestra Sostituisci. Qui, impostate una sostituzione qualsiasi, e comunque premete il comando
    Sostituisci tutto,
    Alt+U.
  3. Premete Esc per uscire dalla finestra, e dovreste tornare alla riga dove eravate prima, con la riga stessa che vi sarà letta.
  4. Uscite ora dal programma, con Alt+F4, rispondendo come volete all’eventuale richiesta di conferma delle modifiche, casomai le aveste effettuate.
  5. Andate sui
    File Recenti
    , e cercate il file da cui siete appena usciti, o comunque trovate il modo per ricaricarlo. Dovreste adesso tornare, anche in questo caso, alla riga registrata in precedenza.

Correggere i difetti.

La procedura così com’è funziona, soprattutto per quel che riguarda il suo scopo iniziale: far tornare alla riga di partenza anche dopo una sostituzione di testo. In realtà, il vero obiettivo era cercare di farvi capire quello che si può fare con le funzioni Evento, e crediamo che l’obiettivo possa dirsi raggiunto.

Così come in altre occasioni sinora, tuttavia, per riuscire a far comprendere tale funzionamento, siamo ricorsi a scorciatoie e semplificazioni. La procedura realizzata, quindi, ha dei difetti, dei quali potreste esservi già resi conto.

Di questi, il più evidente è quello che si riscontra con il ritorno alla riga registrata, una volta usciti dal programma e poi rientrati. Il valore memorizzato, infatti, deve essere necessariamente collegato ad uno specifico file, perché quando se ne carica un altro essa non è più valida, venendo sovrascritta o cancellata.

Questo ed altri problemi saranno risolti più avanti, quando cambieremo drasticamente approccio per quanto riguarda la registrazione delle posizioni in un documento. Nel frattempo, completiamo le informazioni rese, occupandoci di un argomento che avevamo solo preannunciato .

***

L’uso dei parametri nelle funzioni Evento.

Anche le funzioni Evento, così come tutti gli altri elementi di codice utilizzabili con Jaws, possono avere dei parametri. Pure in questo caso, tuttavia, tali parametri sono già decisi dai programmatori di Jaws, almeno nella loro presenza e nel tipo di dati che possono trattare, essendo costituiti da variabili.

Queste variabili, corrispondenti agli eventuali parametri, possono essere personalizzate almeno nel nome, assegnandone uno a nostra scelta. Dovremo comunque rispettare il tipo,
String,
Int,
Handle
o altro, prestabilito per ciascuna di esse dai programmatori di Jaws.

A prescindere dal nome o dal tipo, tali variabili sono
per riferimento
, quindi il loro valore sarà restituito direttamente dalla funzione stessa, senza che noi dovessimo immetterne uno di iniziale. Tali variabili potranno in ogni caso essere utilizzate o rielaborate, anche nel corpo della funzione stessa, così come faremo nel dettaglio più avanti.

Nelle funzioni Evento, i parametri vanno specificati in modo analogo a quello da noi usato nella creazione delle funzioni personalizzate:

  1. Sono posti tra parentesi, con il tipo di dato prima del nome.
  2. Sono separati tra loro da virgole.
  3. Pur essendo per Riferimento, quindi restituiti alla funzione che li propone, non va messo il prefisso
    ByRef
    come succede nelle altre funzioni.

Per essere più chiari, facciamo subito un esempio pratico.

Ci serviremo della funzione
PreProcessKeyPressedEvent
, (EventoAutomaticoPrimaDiEseguireTasti), che si attiva appunto prima che i tasti premuti abbiano effetto sulle funzioni ad essi collegate. In pratica, appena si preme un tasto, prima di fare qualcos’altro, Jaws attiva questa funzione.

Per questo, dentro ad essa vanno messi eventuali controlli sui tasti premuti, così come faremo noi, per poter condizionare le normali funzioni svolte da Jaws.

Questa funzione ha quattro parametri, ma noi imposteremo solo i primi due:

  1. Il codice del tasto, di tipo numerico
    Int
    , il quale restituisce un valore corrispondente al numero del tasto o della combinazione di tasti. Usando questo valore, Jaws può tradurre e pronunciare in ogni lingua i tasti premuti, perché a ciascun valore numerico corrisponde un nome nella lingua in cui è impostata la sintesi vocale.
  2. Il nome del tasto, sotto forma di dato testuale,
    String
    , che sarà però restituito in questo caso nella lingua originale di Jaws, l’inglese.

Come detto, nonostante i parametri siano quattro, noi possiamo specificarne solo due, così come faremo, oppure uno o neanche uno. A ciascuno di questi parametri potremo dare il nome di
Pippo
o di
Pluto
, ma il dato restituito sarà sempre quello deciso dai programmatori, prima il codice numerico, poi il nome testuale. Se poi, come ipotizzato, specificassimo un solo parametro, nella variabile sarebbe registrato soltanto il primo valore, il codice numerico dei tasti premuti.

In particolare, questa sarà l’intestazione della nostra funzione Evento dotata di parametri:


Void Function PreProcessKeyPressedEvent (int iCodiceTasti, String sNomeTasti)

Come potete notare, essa ha l’aspetto di una qualsiasi di quelle realizzate anche da noi. La grande differenza sta nel fatto che questa funzione, una volta attivata dalla pressione di un tasto, assegna da sola, anche senza nemmeno una riga di codice al suo interno, un valore numerico nella variabile
iCodiceTasti
, ed un dato testuale dentro a
sNomeTasti.

Sarà quindi possibile usare direttamente questi dati, usando nel codice le variabili che abbiamo definito come parametri. Nel caso delle funzioni Evento che non avessero parametri, o dove noi non glieli specificassimo, esse avranno effetto solo se noi ponessimo al loro interno un qualche codice che esegua controlli o impostazioni di valori.

Esercizio 8.4.1. La funzione PreProcessKeyPressedEvent () per il Blocco Note.

FileScript.

Notepad.JSS

Nome.

PreProcessKeyPressedEvent

Descrizione.

Pronunciare la versione inglese dei tasti e delle combinazioni che non coinvolgano il TastoJaws.

Ritorni.

Di tipo Void. Nessuno.

Parametri.
  1. iCodiceTasti. Il valore cui corrisponde il tasto o la combinazione premuta. Di tipo Int.
  2. sNomeTasti. Il nome dei tasti premuti, nella forma originale inglese. Di tipo String.
Novità.
  1. La funzione integrata
    StringContains
    , (DatoTestualeContiene), che restituisce la posizione di inizio di una stringa in un testo. Essa ha due parametri, che sono nell’ordine:
    • Il testo in cui cercare la stringa.
    • La stringa da cercare.
  2. Le costanti
    TASTO_JAWS
    e
    INSERT
    , che rappresentano gli omologhi tasti di cui verificare la presenza dentro al testo specificato.
Fasi.
  1. All’interno di un controllo
    If-Then
    , la funzione integrata
    StringContains ()
    verifica che dentro al nome dei tasti premuti non ci sia la dicitura relativa al
    TastoJaws
    , o al tasto
    Insert.
  2. In tal caso, legge il nome in inglese del tasto.
  3. Se invece nessuno dei tasti citati è presente tra quelli letti, vengono lasciate le normali funzioni di lettura dei tasti di Jaws.

Codice.


Void Function PreProcessKeyPressedEvent (int iCodiceTasto, string sNomeTasto)
If !StringContains (sNomeTasto, TASTO_JAWS); se fra quelli premuti non c’è il TastoJaws
&& !StringContains (sNomeTasto, INSERT) Then; e se non c’è neppure il tasto Insert,
SayMessage (OT_ERROR, sNomeTasto); viene letto il nome del tasto in inglese
EndIf; fine controllo tasti
EndFunction

Collaudo.

Se vi sembra di aver già capito come si utilizzano i parametri nelle funzioni evento, allora evitate di inserire il codice della funzione e passate direttamente al prossimo argomento.

Se invece preferite, copiate ed inserite la funzione, e provatela all’interno del Blocco Note.

Dopo il collaudo, cancellate la funzione con
Control+Delete
, perché la sua presenza è assolutamente incompatibile con un normale utilizzo dell’applicativo.

In ogni caso, ora cambiamo decisamente argomento, ed occupiamoci di un aspetto in qualche modo già accennato, ma per la cui importanza vale la pena di segnalare alcune brevi informazioni che completeremo con degli esercizi.

***

Uso dei file JSB con o senza file sorgente.

Se non l’aveste ancora fatto, provate ad analizzare le prime righe del file script Predefinito, Default.JSS.

Dopo alcune righe con il copyright e la versione del programma, iniziano una lunga serie di istruzioni
Include
, come quelle che abbiamo usato per rendere fruibili le nostre versioni dei file con le variabili globali, le costanti o i messaggi.

Oltre a queste, potrete notare un’altrettanto lunga serie di altre istruzioni,
Use
, (Utilizza), di cui andremo a parlare ora.

In queste ultime, la sintassi è del tutto analoga a quella di Include, con la parola chiave
Use
seguita da un nome di file tra virgolette, i quali hanno tutti in comune la stessa estensione,
JSB
. Questi, in effetti, sono tutti file script già compilati, il cui codice costituito da script e funzioni andrà ad aggiungersi a quello del file script in cui sono stati inseriti, in questo caso nel file Predefinito.

L’utilizzo di Questi
File Script Esterni
consente di aggiungere anche una grande quantità di codice, senza appesantire troppo il file principale e semplificando molto la sua gestione. Ciascun file JSB, infatti, potrà avere il proprio file sorgente JSS, dove poter più agevolmente andare ad effettuare eventuali modifiche o controlli successivi.

Analizzeremo tra poco i vari aspetti dell’uso dei file script esterni, valutando vantaggi e svantaggi di tale soluzione. Prima di questo , tuttavia,

è necessario aprire una parentesi, parlando di un concetto che è forse emerso tra le righe delle nostre spiegazioni, ma che non è mai stato direttamente espresso.

I file indispensabili al funzionamento di JAWS.

Fin qui abbiamo conosciuto vari tipi di file che sono coinvolti nel funzionamento di Jaws, e molti altri ce ne sono che neppure tratteremo. Di questi, i cui principali li elenchiamo di seguito, ciascuno con l’estensione che li caratterizza:

  • File sorgenti, JSS.
  • File Documentazione, JSD
  • FileCostanti e Variabili Globali, JSH.
  • File Messaggi, JSM.
  • File Tasti, JKM.
  • File Compilati, JSB.

Di questi, tuttavia, quali sono davvero indispensabili, affinché durante il nostro utilizzo del PC, i nostri script e le nostre funzioni compiano il loro lavoro?

La risposta è semplice:

  1. Il file Compilato, con estensione
    JSB
    , che contiene tutto il codice scritto nel file
    JSS
    , ed ingloba il contenuto degli eventuali file Costanti e delle Variabili Globali, dei file Messaggi e di ogni altro dato che si renda disponibile tramite altri tipi di file.
  2. Il file Tasti, con estensione
    JKM
    , che dice a Jaws quali script attivare alla pressione di determinati tasti, secondo la modalità di abbinamento che abbiamo analizzato lo scorso capitolo.

L’aspetto importante da sottolineare è che, appunto, di norma servono entrambi , almeno in tutti i casi nei quali ci sia anche solo uno script nel codice sorgente. Il file tasti, infatti, al momento della compilazione non è inglobato nel file JSB, così come succede a tutti gli altri file coinvolti, e quindi la sua presenza come file a sé stante è di solito necessaria.

Anche se tali affermazioni potrebbero bastare per occuparci dell’argomento, vi invitiamo ad eseguire ugualmente anche le esercitazioni poste di seguito. Al di là della controprova sulle informazioni date, infatti, tali esercizi sono necessari anche a predisporre del materiale che sarà usato solo più avanti, parlando di un altro argomento correlato.

Esercizio 8.5.2. La funzione AutoStartEvent () in Registrazione Azioni Utente.

FileScript.

Psr.JSS

Nome.

AutoStartEvent

Descrizione.

Legge una dicitura all’avvio dell’applicazione.

Ritorni.

Di tipo Void. Nessuno.

Novità.
  1. La costante
    AUTOMATICA
    , che contiene la dicitura da leggere all’avvio del programma.
Note.

La funzione Evento va inserita nel file script relativo alla
Registrazione Azioni Utente
, un’applicazione compresa nel sistema operativo Windows, la quale ci interessa soltanto per il fatto di non avere dei propri script. Questo ci consentirà dunque di crearli da zero, e poterne poi pertanto disporre a piacimento.

Per inizializzare il lavoro, seguite questi passi:

  1. Attivate la Registrazione Azioni Utente, cercandola nel campo di ricerca di Windows.
  2. Con
    TastoJaws+0
    , aprite il relativo file script, che come detto al primo ingresso risulterà vuoto.
  3. Inizializzate lo script, premendo
    Shift+Control+1
    e
    Shift+Control+2
    per inserire l’etichetta di inizio codice e le dichiarazioni iniziali. In questo caso, non essendoci script originali, risalite sino alla prima dichiarazione
    Include
    , quella del file costanti predefinito, e togliete il carattere Punto e Virgola ad inizio riga.
  4. Inserite il codice della funzione, che presentiamo di seguito, quindi salvate compilando, ed infine proseguite con la seconda parte della procedura.

Codice.


Void Function AutoStartEvent ()
SayMessage (OT_ERROR, AUTOMATICA); legge il messaggio nel tono minore
EndFunction

Esercizio 8.5.3. Lo script LetturaDiretta.

FileScript.

Psr.JSS

Nome.

LetturaDiretta

Sommario.

Legge una stringa

TastiAttivazione.

F8

Novità.
  1. La costante
    DIRETTA
    , che contiene il testo da leggere con la pressione del tasto funzione.

Codice.


Script LetturaDiretta ()
SayMessage (OT_HELP, DIRETTA); legge la dicitura col tono della voce primaria di Jaws
EndScript

Collaudo.

Se la compilazione non dà problemi, il test della procedura sui file indispensabili serve, come detto, a predisporre il materiale per un futuro esercizio.

Quindi, seguite questi passi:

  1. Uscite chiudendo l’Editor di Jaws, ed entrate nel programma
    Registrazione Azioni Utente
    , secondo le modalità già proposte nelle Note al precedente esercizio.
  2. All’ingresso, tra le altre informazioni rese dal programma, la dicitura immessa nella funzione
    AutoStartEvent ()
    dovrebbe esservi recitata con un tono minore. Premete quindi il tasto funzione
    F8
    , e dovrebbe esservi proposta la seconda dicitura per la lettura diretta, decretando così il corretto funzionamento della procedura realizzata.
  3. Portatevi poi nella cartella delle
    Impostazioni personali
    di Jaws, sfruttando il collegamento nelle sue
    Utility
    . Premete la lettera
    P
    , finché non ci si porta sui file che hanno come nome
    Psr.
  4. Tra questi, cancellate i file
    Psr.JSD
    e
    Psr.JSS
    , controllando che rimangano nella cartella gli omonimi file con estensione
    JSB
    e
    JKM.
  5. Tornate alla
    Registrazione Azioni Utente
    e ripetete la prova illustrata al punto 2. Se questa riesce, avrete la conferma che i file rimasti sono quelli che servono al funzionamento di Jaws, in questo caso
    Psr.JSB
    e
    Psr.JKM.
  6. Qui potete anche interrompere la prova. Se volete, tuttavia, effettuate una copia dei due file citati al punto 4, salvandoli in un’altra cartella, e proseguite con il test.
  7. Tornate nella cartella delle
    Impostazioni personali
    , e Provate a cancellare ora il solo file
    Psr.JKM
    . Se tornate all’applicazione, all’ingresso dovrebbe esservi recitata la dicitura automatica, ma se premete il tasto funzione
    F8
    non accadrà nulla e nulla vi sarà ulteriormente recitato. Quindi, l’assenza di tale file disabilita lo script creato.
  8. Ora, tornate alla cartella, ripristinate la copia del file JKM, e cancellate quella del file compilato,
    Psr.JSB
    . Se tornate all’applicazione, stavolta all’ingresso non vi sarà letto nulla, poiché manca il file che contiene la funzione Evento, mentre se premete
    F8
    , almeno vi sarà letto un avviso del tipo:
  9. "Chiamata ad uno script sconosciuto per..."

  10. Ricapitolando, con queste prove dapprima avete avuto la conferma che quando si tratta di attivare uno script di un codice, il file JSB da solo non basta, in quanto è il file JKM che, come farebbe un vigile, dirige il traffico delle pressioni della tastiera verso i file script compilati, quelli con il codice da eseguire. Poi, vi è stato confermato pure che il file con estensione JKM viene sempre letto da Jaws, anche in assenza del file JSB, ma senza quest’ultimo non fa nulla perché il codice da eseguire è contenuto nell’altro file.
  11. In ogni caso, tornate nella cartella e ripristinate anche la copia del file JSB, tornando ad avere presenti entrambi i file indispensabili. A questi, ed alla Registrazione Azioni Utente, torneremo tra un po’ per completare l’argomento. Nel frattempo, continuiamo a parlare dell’utilizzo dei file compilati, e come renderli disponibili in altri codici.

Generare un File Script esterno.

Dopo aver compreso come interagiscano i vari file creati da Jaws, il modo più semplice per far capire cosa si nasconda dietro a quelle istruzioni
Use
, seguite da un nome di file
JSB
è, come sempre, creare noi un nostro file script esterno, che collegheremo poi allo script principale tramite la citata istruzione. In particolare, torneremo per questo al file script del Blocco Note, creandone una copia e poi collegandola al file
Notepad.JSS.

Questo lavoro, va detto, non lo faremo comunque solo a scopo di prova, ma per far raggiungere al file script del Blocco Note la sua forma definitiva. Al momento, infatti, esso contiene procedure e funzioni che andrebbero poste nel file script Predefinito, e che abbiamo messo lì solo temporaneamente per poterle proporre in una forma semplificata più adatta ad essere compresa da tutti.

Per questo, dapprima copieremo l’intero file script in una versione secondaria, alla quale poi faremo assumere il ruolo del file esterno collegato. Proseguiremo, quindi, togliendo dal file script del Blocco Note le funzioni inutili, apporteremo qualche modifica , ed infine le faremo eseguire collegandole dalla copia esterna appena creata.

Esercizio 8.5.5. Il file script duplicato BloccoNote.JSS.

FileScript.

Notepad.JSS

Note.

Per creare in modo corretto una copia esterna del file script per il Blocco Note, seguite questi passi:

  1. Entrate nell’Editor di Script, da qualsiasi applicativo, aprite un file con
    Control+O
    , e scegliete come tipo
    File di documentazione (*.jsd).
  2. Nella casella di elenco, selezionate e poi aprite il file
    Notepad.JSD.
  3. Agite sul comando
    Salva con nome
    del menu File, richiamabile nelle versioni di Jaws dalla 17 in poi con
    Alt+F, lettera C
    , mentre per Jaws sino alla 16 con
    Alt+F, lettera A.
  4. Nella schermata che si apre inserite un nome che sia diverso da quello originale, come ad esempio
    BloccoNote
    , e poi premete Invio.
  5. Chiudete il file con
    Control+F4
    , premete di nuovo
    Control+O
    , ed andate direttamente nella casella di elenco per selezionare e poi aprire il file
    Notepad.JSS.
  6. Ripetete i passi 3 e 4, salvando il file aperto ancora con il nome
    BloccoNote
    . Stavolta dovreste sentire l’avviso
    "Compilazione completata".
  7. Se la compilazione va a buon fine, rimarrà aperto il file
    BloccoNote.JSS
    . Qui, iniziate individuando, e cancellando con il comando
    Control+Delete
    , gli script
    SalvaConNome ()
    , e
    ValoreLunghezza ().
  8. Sempre nello stesso file, portatevi ora nello script
    TrovaTesto ()
    , e cancellatene le tre seguenti righe:
  9. 
    TypeKey ("Alt+M"); richiama il menu Modifica
    Pause (); sospende temporaneamente lo script
    TypeString ("O"); richiama il comando Trova dal menu
    

  10. Al loro posto, limitatevi ad inserire l’istruzione seguente, la quale chiama una nostra funzione che creeremo tra poco:
  11. 
    ComandoTrova ()
    


    ; chiama l’apposita funzione

  12. Se il file script contiene soltanto i vostri script, interrompete la lettura di questi passi e saltate direttamente alla verifica del codice.
  13. Altrimenti, portatevi al punto d’inizio dei vostri script, che dovrebbe essere contrassegnato dall’etichetta del riferimento personale. Dando per scontato che da lì all’inizio del file vi sia il codice originale di Jaws, selezionate da quel punto sino all’inizio del file script e cancellate tutto il codice con Delete.
  14. Poiché assieme al resto degli script originali abbiamo cancellato anche la dichiarazione
    Include
    con la quale si inseriva nel file script il contenuto del file costanti predefinito, andate sulle nostre dichiarazioni iniziali e togliete il carattere Punto e Virgola dall’inizio della riga dell’istruzione che coinvolge il file
    HJConst.JSH.
  15. Richiamate il comando
    Sincronizza Documentazione
    dal menu File, che ricordiamo non avere scelte rapide, ed attendete che vi sia proposto l’avviso
    "Sincronizzazione completata".
  16. Al termine, il codice contenuto nel file script esterno dovrebbe essere quello proposto nella sezione seguente:

Codice.


; _Ps Codice
Include "HJConst.JSH"; file costanti predefinito di Jaws
Include "_PsConst.JSH"; File personale delle costanti
Include "_PsGlobals.JSH"; file personale delle variabili globali
Include "_PsMessages.JSM"; File personale dei messaggi
Script TrovaTesto ()
SpeechOff (); spegne la sintesi
PerformScript SalvaContrassegno (); esegue lo script per salvare la posizione del cursore
ComandoTrova (); chiama l’apposita funzione
SpeechOn (); riattiva la sintesi
EndScript
Script PronunciaValoreRiga ()
SayString ("Riga" + IntToString (GetCursorRow ())); legge il numero della riga corrente
EndScript
Int Function PosizioneCursore ()
Return GetCursorRow (); restituisce la riga su cui è posto il cursore
EndFunction
Int Function MuoveAllaRiga (int iValore)
Var Int iTotale; lunghezza complessiva del documento
If !iValore ; se non è stato specificato alcun numero di riga,
|| iValore == PosizioneCursore () Then; oppure se la riga richiesta è la stessa dell’attuale,
Return FALSE; restituisce un risultato nullo
EndIf; fine controllo validità valore
Let iTotale = ValoreLunghezza (); rileva il valore complessivo
If !iTotale ; se la lunghezza del file non è stata rilevata,
||iValore < iTotale Then; oppure se il valore è inferiore alla lunghezza del documento,
SpeechOff (); spegne la sintesi
TypeKey (CONTROL_G); richiama il comando di spostamento alla riga
Pause ()
TypeString (IntToString (iValore)); immette la riga a cui portarsi
EnterKey (); simula la pressione di Invio
Pause ()
SpeechOn (); riattiva la sintesi
Return TRUE; restituisce l’esito positivo
Else; se invece il numero di riga è maggiore o uguale a quello delle righe totali,
JAWSBottomOfFile (); si porta alla fine del documento,
SayMessage (OT_ERROR, hlpFineFile, hlpFineFile_corto); avvisa dello spostamento,
Return FALSE; e restituisce un risultato nullo
EndIf; fine controllo righe totali
EndFunction
Void Function ControllaRitorno ()
If gnAnnullaRitorno Then; se il divieto al ritorno all’ultima posizione è attivo,
Return; interrompe il flusso
EndIf; Fine controllo divieto
If MuoveAllaRiga (gnUltima) Then; se lo spostamento riesce,
; legge il numero di riga cui ci si è portati
SayFormattedMessage (OT_ERROR, msg2, msgSecondo, RIGA, gnUltima)
EndIf; fine controllo spostamento
SayLine (); legge la riga corrente
Let gnAnnullaRitorno = TRUE; attiva il divieto al ritorno all’ultima riga
EndFunction
Void Function ScreenStabilizedEvent ()
If SiamoNellaFinestra (EDITING) Then; se si è nella finestra corretta,
If PosizioneCursore () != gnUltima Then; se la riga attuale non è quella memorizzata,
Let gnUltima = PosizioneCursore (); aggiorna il valore
EndIf; fine controllo riga
EndIf; fine controllo finestra
EndFunction
Void Function AutoStartEvent ()
If SiamoNellaFinestra (EDITING) Then; se si è nella finestra corretta,
ControllaRitorno (); verifica il possibile ritorno all’ultima riga registrata
EndIf; fine controllo finestra
EndFunction
Void Function AutoFinishEvent ()
; consente un successivo ritorno all’ultima riga, disattivandone il divieto
Let gnAnnullaRitorno = FALSE
EndFunction
Void Function WindowActivatedEvent ()
Delay (ATTESA); ritarda il flusso per i decimi di secondo impostati
If SiamoNellaFinestra (EDITING) Then; se si è nella finestra primaria,
ControllaRitorno (); verifica il possibile ritorno all’ultima riga registrata
ElIf SiamoNellaFinestra (SOSTITUISCI) Then; se si è in quella di sostituzione,
Let gnAnnullaRitorno = FALSE; consente di tornare all’ultima riga, disattivandone il divieto
EndIf; fine controllo finestra
EndFunction

Collaudo.

  1. Salvate e, se sentite l’avviso della compilazione corretta, chiudete il file con
    Control+F4
    , altrimenti ripetete i passi dall’1 all’11 e provate nuovamente a compilare.
  2. Quando è tutto a posto, torniamo al file script principale del Blocco Note, dove dovremo fare anche lì i necessari adattamenti, iniziando col riconvertire uno script in funzione.

Esercizio 8.5.6. La funzione riconvertita ComandoTrova.

FileScript.

Notepad.JSS

Nome.

ComandoTrova

Descrizione.

Attiva il comando di ricerca testo dell’applicativo.

Ritorni.

Di tipo Void. Nessuno.

Note.
  1. Se non vi siete tornati dopo aver chiuso il file esterno, aprite il file principale
    Notepad.JSS.
  2. Scendete sino alla riga di intestazione dello script
    TrovaTesto ()
    . Qui è necessario trasformare lo script in funzione ma, prima di aprire la schermata di modifica, dovremo fare quasi tutto a mano. Iniziate, quindi, con il cancellare la prima riga con il titolo, sostituendola con la seguente:
  3. 
    Void Function ComandoTrova ()
    

  4. A questo punto ci sarebbero da eliminare anche le istruzioni che spengono la sintesi,
    SpeechOff ()
    , e quella che la riattiva,
    SpeechOn ()
    . Siccome in realtà le dovremmo sostituire con la chiamata ad un’altra funzione, che realizzeremo però solo nel prossimo capitolo, per il momento limitiamoci a renderle inattive ponendo un carattere Punto e Virgola davanti ad entrambe.
  5. Appena sotto alla riga con lo spegnimento della sintesi, eliminate l’istruzione:
  6. 
    PerformScript SalvaContrassegno (); esegue lo script per salvare la posizione del cursore
    

  7. Portatevi alla fine dell’elemento di codice e, poiché ne abbiamo convertito il tipo, sostituite l’istruzione
    EndScript
    con
    EndFunction.
  8. Solo adesso, se volete, aprite la schermata di modifica dell’elemento, ma solo per scrivere la nuova descrizione in quanto non vi sono né ritorni, né parametri.

Codice.


Void Function ComandoTrova ()
; SpeechOff (); spegne la sintesi
TypeKey ("Alt+M"); richiama il menu Modifica
Pause (); sospende temporaneamente lo script
TypeString ("O"); richiama il comando Trova dal menu
; SpeechOn (); riattiva la sintesi
EndFunction

Collaudo.

.

  1. Come spesso capita, ed ora a maggior ragione, il test lo faremo solo dopo aver compiuto il resto delle modifiche.

Esercizio 8.5.7. L’aggiornamento del file script Notepad.JSS.

FileScript.

Notepad.JSS

Note.
  1. Per completare l’aggiornamento del file script, individuate l’inizio degli script personali, e posizionatevi nella riga sotto all’ultima istruzione
    Include
    , quindi inserite l’istruzione che collega il file esterno a quello attuale, che avrà la seguente forma:
  2. 
    Use "BloccoNote.JSB"; collega il file script esterno
    

  3. Portatevi all’interno dello script
    PronunciaValoreRiga ()
    . Selezionatene il contenuto con
    Control+R
    , quindi cancellate premendo semplicemente il tasto Delete. Fate attenzione ad usare solo questo sistema, non la rimozione tramite la scelta rapida apposita, perché quest’ultima ne eliminerebbe anche l’abbinamento con i tasti di Attivazione nel file
    Notepad.JKM.
  4. Eliminate anche la funzione
    PosizioneCursore ()
    , con le stesse modalità illustrate al punto precedente.
  5. Scendete ancora sino alla funzione
    MuoveAllaRiga ()
    . In pratica, noi dovremo cancellare tutto il codice presente da qui alla fine del file ma, usando i tasti di movimento
    Windows+FrecciaGiù
    , o
    Windows+FrecciaSu
    , prima controllate che ci siano le seguenti funzioni:
    • ControllaRitorno ()
    • ScreenStabilizedEvent ()
    • AutoStartEvent ()
    • AutoFinishEvent ()
    • WindowActivatedEvent ()
  6. Per rimuoverle, potete selezionarle una ad una, e cancellarle poi con Delete, oppure selezionarle tutte a partire da
    MuoveAllaRiga ()
    sino alla fine del documento, cancellandole poi tutte assieme con Delete. Come già detto, l’importante è non utilizzare il comando di rimozione script.
  7. In ogni caso, agite sul comando
    Sincronizza Documentazione
    , nel menu File, rimuovendo così tutti i riferimenti agli script che la procedura invita a selezionare, quindi avviando la sincronizzazione, al cui termine salvate infine con
    Control+S.
  8. Alla fine del processo, la parte da voi personalizzata del file script, dovrebbe avere l’aspetto proposto di seguito, a parte l’etichetta di inizio codice Personale e le prime dichiarazioni
    Include
    , che in questo caso sono omesse in quanto, se ricordate, esse dipendono dalla personale configurazione.

Codice.


Use "BloccoNote.JSB"; collega il file script esterno
Script SalvaConNome ()
SpeechOff (); spegne la sintesi
TypeKey ("Alt+F"); richiama il menu File
Pause (); sospende temporaneamente lo script
PriorLine (); si muove nel menu di una voce verso l’alto, simulando il tasto FrecciaSu,
PriorLine (); si muove di una seconda voce,
PriorLine (); di una terza,
PriorLine (); ed infine di una quarta, arrivando sulla voce desiderata,
EnterKey (); dove si simula la pressione del tasto Invio
SpeechOn (); riattiva la sintesi
EndScript
Int Function ValoreLunghezza ()
Return GetLineCount () +1; restituisce il numero totale delle righe, aggiungendone un’unità
EndFunction
Void Function ComandoTrova ()
; SpeechOff (); spegne la sintesi
TypeKey ("Alt+M"); richiama il menu Modifica
Pause (); sospende temporaneamente lo script
TypeString ("O"); richiama il comando Trova dal menu
; SpeechOn (); riattiva la sintesi
EndFunction

Collaudo.

  1. Dopo aver compilato correttamente, se adesso riaprite il Blocco Note con un file non vuoto, potrete notare che funzionano sia lo script cancellato dal file primario,
    PronunciaValoreRiga ()
    , sia quello convertito da script a funzione,
    TrovaTesto ()
    . Si attiverà anche la procedura di ritorno all’ultima riga memorizzata, nel caso di riapertura nella stessa sessione o di sostituzione di testo.
  2. Avrete così la conferma che il nostro codice, anche collegato ad un file esterno tramite l’istruzione
    Use
    , fa funzionare ugualmente i vari elementi.
  3. In generale, come si accennava all’inizio, anche l’uso dei file script esterni ha qualche difetto, che però analizzeremo man mano che ne avremo l’occasione. Nel frattempo, iniziamo ad analizzarne i vantaggi, esplorando una possibilità che è opportuno almeno conoscere.

Creare un file script non avendo il file sorgente.

Per quanto riguarda gli script di un qualche applicativo, può capitare che si disponga soltanto di un file compilato con estensione
JSB
, e casomai di un file Tasti
JKM
, senza tuttavia disporre di un file sorgente
JSS
. Come detto, tali script funzionerebbero perfettamente, perché i file indispensabili sarebbero presenti, ma non si potrebbe effettuare ad essi delle modifiche, le quali andrebbero poi ricompilate nella nuova versione.

Se però vi fosse l’esigenza di creare un proprio codice, i cui elementi possano funzionare in parallelo a quelli già presenti nel file compilato esistente, c’è comunque un metodo per farlo: trattare quest’ultimo come fosse un file script esterno, collegandolo ad un nuovo file script principale tramite l’istruzione
Use.

Per dimostrare come si realizza questa soluzione, torneremo ad utilizzare i due file rimasti tra quelli creati negli esercizi con la Registrazione Azioni Utente,
Psr.JSB
e
Psr.JKM
. Nel dettaglio, rinomineremo il file compilato, mentre ci serviremo del file tasti esistente, lasciandolo così com’è al momento attuale.

Come nuovo elemento di codice, creeremo un altro semplice script di lettura, che ci serve soltanto per confermare il suo funzionamento assieme agli altri due elementi inclusi nel file script originale già compilato.

Esercizio 8.5.9. Il nuovo file script della Registrazione Azioni Utente.

FileScript.

Psr.JSS

Note.

Per realizzare la modifica preannunciata, seguite scrupolosamente questi passi:

  1. Portatevi nella cartella delle
    Impostazioni personali
    , utilizzando i collegamenti tra le
    Utility di
    Jaws. Qui, rinominate l’esistente
    Psr.JSB
    , sostituendolo con il nuovo nome
    AzioniUtente.JSB.
  2. Controllate che nella stessa cartella dei file per l’Utente vi sia rimasto il file
    Psr.JKM
    , e che non siano presenti altri file dal nome
    Psr
    , i quali andrebbero casomai cancellati.
  3. Avviate il programma
    Registrazione Azioni Utente
    , secondo le modalità già proposte.
  4. Con
    TastoJaws+0
    richiamate l’Editor di Script, che dovrebbe caricarsi aprendo un file script, che dovrebbe essere vuoto, dal nome
    Psr.JSS.
  5. Così come fatto per la prima versione, inizializzate lo script premendo in sequenza i tasti
    Shift+Control+1
    e
    Shift+Control+2
    , per inserire l’etichetta degli script personali e le dichiarazioni di inclusione dei file esterni. Anche in questo caso, risalite sino alla prima istruzione
    Include
    , quella che inserisce il file costanti predefinito
    HJConst.JSH
    , e togliete il carattere Punto e Virgola ad inizio riga.
  6. Nella riga immediatamente sotto all’ultimo comando
    Include
    , scrivete la seguente istruzione:
  7. 
    Use "AzioniUtente.JSB"; collega il file script compilato precedente
    

Collaudo.

Anche se il nuovo file script è così inizializzato, ancora il collegamento non funziona perché, com’è logico che sia, esso necessita di almeno un nuovo elemento di codice posto all’interno del file script. Diversamente, non avrebbe senso usare questo metodo, perché converrebbe usare il solo file compilato precedente.

Per questo, se volete, salvate compilando, ma per la prova attendete di completare l’opera.

Esercizio 8.5.10. Lo script MioCodice.

FileScript.

Psr.JSS

Nome.

MioCodice

Sommario.

Legge una dicitura dal codice personale.

TastiAttivazione.

Control+F8

Novità.
  1. La costante
    CODICE
    , che contiene l’omonimo testo da far leggere alla pressione dei tasti di attivazione.
Note.
  1. Il contenuto dello script sarà un comando di lettura di un testo con il tono minore.

Codice.


Script MioCodice ()
SayMessage (OT_HELP, CODICE)
EndScript

Collaudo.

  1. Ora, se provate a salvare ed è tutto a posto, tornate alla Registrazione Azioni Utente.
  2. Dovreste dapprima sentire la dicitura
    Automatica
    , letta dalla funzione Evento presente nel vecchio script. Quindi, premendo
    F8
    , dovrebbe essere letta la dicitura
    Diretta
    , che proviene anche questa dalla versione precedente.
  3. Poi, con
    Control+F8
    , si potrà eseguire lo script appena inserito nel file sorgente, il cui messaggio dovrebbe esservi correttamente proposto.
  4. Ecco, quindi, realizzata la fusione tra il file compilato originario ed i vostri script. Si tratta senz’altro di una situazione limite, poiché l’ottimale resta sempre disporre del file sorgente originale, ma l’importante era chiarire che pure in questo stato si può sfruttare al meglio il materiale a disposizione.

Testare nei file script esterni le nuove procedure.

Oltre al caso appena illustrato, quello di un file compilato di cui non si dispone il sorgente, l’uso dei file
JSB
collegati come esterni ad un file script principale è una soluzione che può tornare utile anche in altre circostanze.

Ad esempio, sarà capitato anche a voi che si presenti un errore durante il salvataggio di un file script, il quale non consente di eseguire una corretta compilazione del codice. Forse nei file script per gli applicativi si nota meno, ma se l’errore si fosse presentato modificando il file script Predefinito,
Default.JSS
, potreste esservi accorti anche voi che gli script e le funzioni con le quali abbiamo personalizzato Jaws, a quel punto, non funzionano più.

Questo succede perché, quando Jaws rileva un’anomalia del codice che ne impedisce la corretta compilazione, cancella la versione compilata del file script aperto nell’Editor, quella con l’estensione
JSB
che si trova nella cartella dei File per l’Utente, e che contiene le nostre personalizzazioni.

Tutti gli altri script originali, quelli presenti ad esempio nel file script Predefinito, continuano però a funzionare regolarmente, perché Jaws va a leggerli nei file compilati presenti nella cartella delle
Impostazioni condivise
. In questa cartella, che abbiamo già incontrato negli scorsi capitoli, i nostri script non ci sono, poiché salvati di norma solo in quella per l’Utente, e proprio per questo non più rilevabili da Jaws.

Si tratterebbe, per la verità, di un problema temporaneo: quando risolviamo l’errore nel codice sorgente, e la compilazione riesce, si ricrea il file con estensione
JSB
nella cartella con i file Utente, e quindi anche le nostre personalizzazioni riprendono a funzionare.

Va inoltre ricordato, a chi non la conosca, che esiste sempre la possibilità di consentire una pressione di tasti senza che Jaws intervenga, grazie al comando
Passa tasto a
, attivabile con la combinazione
TastoJaws+3
. In altre parole, quando si preme tale combinazione, la pressione di tasti successiva sarà passata all’applicazione sottostante senza coinvolgere Jaws e, per questo, evitando l’eventuale blocco causato dal mancato funzionamento di un nostro script.

Questo inconveniente, quindi, finché si aggiungono pochi script o funzioni, può non avere molto peso. Quando però si iniziano a creare script abbinati anche a tasti o combinazioni di uso comune, come ad esempio
Control+F
o
Control+H
, nel momento in cui si verifica un errore, e finché lo stesso non sia risolto, tali tasti non sono più utilizzabili, proprio perché non riescono a raggiungere gli script o le funzioni ad essi collegati.

Una soluzione a questo problema potrebbe essere quello di isolare le procedure che si sta realizzando in un file esterno, che così in caso di errore sarebbero le uniche a non essere disponibili. Si tratta di una soluzione solo teorica, che spesso non si sfrutta per questioni di tempo, ma che ci fornisce lo spunto per illustrare un’altra modalità per collegare tale codice al file script principale.

Scendendo nel dettaglio, tale opzione può risultare valida per le funzioni, che abbiamo già analizzato non avere differenze di effetto tra l’essere inserite nel file script principale o in quello collegato. In caso di script, quando sono realizzati dentro ad un file esterno, per garantirne l’efficacia è invece necessario compiere un ulteriore passaggio, oltre alla procedura di spostamento descritta in precedenza.

Jaws, infatti, crea gli script inserendo l’abbinamento tra gli stessi script ed i loro tasti di attivazione in un file tasti che, ovviamente, avrà il nome del file script aperto in quel momento. Se però questo nome non corrisponde ad nessuna applicazione, Jaws non leggerà mai questo file tasti, di fatto impedendo che uno script creato in un file script esterno sia richiamato direttamente.

Facciamo un esempio per capirci meglio, utilizzando lo script
LetturaDiretta ()
. Se ricordate, lo avevamo creato da un’altra parte, ma ora esso si trova dentro ad un file esterno che ha come base il nome
AzioniUtente.

Se noi l’avessimo generato direttamente da dentro un file che si chiamava
AzioniUtente.JSS
,l’assegnazione con il suo tasto di attivazione,
F8
, sarebbe stata scritta dentro ad un file tasti che Jaws avrebbe denominato
AzioniUtente.JKM.

Il problema, in questo caso, è che un applicativo denominato
AzioniUtente
non esiste, perché il programma in cui il nostro script dovrebbe funzionare viene riconosciuto da Jaws con il nome
Psr.

Poiché tutti i file che Jaws abbina al programma
Registrazione Azioni Utente
hanno proprio questa sigla di tre lettere a fungere da base, la sequenza di eventi sarebbe quella posta di seguito:

  1. Da dentro il programma
    Registrazione Azioni Utente
    ,noi premiamo
    F8
    , per eseguire lo script
    LetturaDiretta ().
  2. Jaws inizia a cercare se quel tasto funzione corrisponde ad un’assegnazione dentro ad un file che dovrebbe chiamarsi
    Psr.JKM.
  3. Se non trova il file, oppure se non vi trova alcun riferimento al tasto premuto,
    F8
    , passa a cercare nel file tasti Predefinito, che si chiama
    Default.JKM.
  4. Se anche qui Jaws non trova nessuno script abbinato a tale tasto, allora lo passa al programma il quale, nel caso specifico, non produce nulla e non emette alcun avviso.

Come avete potuto notare, quindi, le assegnazioni di tasti sono riconosciute da Jaws solo nel caso in cui siano impostate direttamente da dentro al file script collegato al programma di proprio interesse, che in questo caso si chiamerebbe
Psr.JSS.

Per ovviare a tale inconveniente, ci sono due strade principali:

  1. Copiare a mano nel file tasti del file script principale dell’applicativo, o del file script Predefinito di Jaws, l’abbinamento tra tasti e script.
  2. Creare uno script nel file principale dell’applicativo, o nel file script Predefinito, che abbia come unica istruzione il richiamo dello stesso elemento di codice, tramite il comando
    PerformScript
    , e lasciare che sia Jaws ad annotarsi la combinazione che attiva lo script nei file tasti che poi andrà a leggersi.

Sebbene sembrerebbe la più semplice, ci sentiamo di sconsigliare la prima, soprattutto perché obbliga ad inserire nei file tasti di Jaws dei contenuti che in teoria non dovrebbero essere lì. Questo fatto, oltre a creare sempre la possibilità di fare degli errori agendo manualmente, sottrae al controllo del programma di sintesi vocale tali abbinamenti, impedendo poi di modificarli tramite le normali funzioni di Jaws.

La seconda soluzione è invece quella di gran lunga da preferire, anche perché utilizzata nei file script originali di Jaws. Per questo, vi proponiamo di seguito il codice che sarebbe da inserire nel file script principale, in questo caso
Psr.JSS
, per garantire il funzionamento dello script citato:


Script LetturaDiretta ()
PerformScript LetturaDiretta ()
EndScript

In questo caso, la sequenza proposta in precedenza, seguirebbe ora questi passi:

  1. Da dentro il programma
    Registrazione Azioni Utente
    ,noi premiamo
    F8
    , per eseguire lo script
    LetturaDiretta ().
  2. Jaws inizia a cercare se quel tasto funzione corrisponde ad un’assegnazione dentro ad un file che dovrebbe chiamarsi
    Psr.JKM.
  3. Stavolta, Jaws trova nel file tasti l’assegnazione che attiva lo script
    LetturaDiretta ()
    presente nel file script principale.
  4. Da qui, viene eseguito un altro script con lo stesso nome, che però stavolta è quello presente nel file script collegato,
    AzioniUtente.JSB
    , che legge la dicitura da noi predisposta e interrompe il flusso.

Ricapitolando, nel caso di elementi di codice inseriti nei file script esterni, e collegati ad uno principale tramite il comando
Use:

  1. In caso di Funzioni, la loro efficacia rimane la stessa, sia che si trovino nel file script principale dell’applicativo, sia che siano poste nel file script esterno.
  2. Per quel che riguarda gli Script, il codice si può inserire anche nel file script esterno, ma in tal caso è necessario realizzarne uno con lo stesso nome all’interno del file script principale, il quale abbia come unica istruzione un richiamo allo stesso elemento di codice tramite il comando
    PerformScript.

***

Gli Oggetti.

Gli elementi che si trovano dentro ad una finestra, così come ne avevamo accennato nel Capitolo 6, possono essere delle altre finestre, oppure no. In quest’ultimo caso, se non sono finestre, allora sono degli
Oggetti
. La definizione di oggetto in informatica, ed ancor più in programmazione, ci porterebbe troppo lontano da quello che abbiamo fatto sinora, e solo per questo motivo non approfondiremo l’argomento sul piano teorico.

Ci basterà dire che un Oggetto è un elemento autonomo, e che possiede delle caratteristiche o dei dati, i quali si chiamano
Proprietà
. Esso può avere al suo interno anche alcune tecniche o funzioni che possono attivarsi per, o far attivare, l’oggetto, e questi sono chiamati
Metodi.

Le proprietà possono sia restituire informazioni riguardanti l’Oggetto, sia essere modificate dall’esterno per far cambiare i valori dell’Oggetto stesso.

Per quello che interessa a noi, negli script per Jaws l’Oggetto è anche un tipo di dato, che viene usato come parola chiave con il suo termine inglese
Object
. Pertanto, una variabile di tipo Object si dichiara nel modo seguente:


Var Object oWord

Abbiamo usato come esempio non ha caso il famoso programma di videoscrittura del pacchetto Office di Microsoft, perché anch’esso, nel suo assieme, è un Oggetto. Grazie alle caratteristiche di accessibilità dei prodotti Microsoft, le funzioni di Jaws sono in grado di leggere anche direttamente dalle proprietà di Word, come di Excel e gli altri programmi del pacchetto, le informazioni che servono al loro utilizzo con il programma di sintesi vocale.

Per quanto riguarda la costruzione dei nostri script, sapere che esistono gli oggetti a noi serve se non altro per avere un’opzione in più. Infatti, quando un tipo di informazione non viene resa dalle funzioni che leggono i dati delle finestre, allora si può provare ad utilizzare le funzioni legate agli oggetti.

Questa doppia possibilità è sancita anche dalla similitudine nei nomi di alcune funzioni, che hanno tra loro la sola differenza della parola chiave,
Window
o
Object.

Uno di questi casi è quello delle funzioni integrate
GetWindowName ()
, che abbiamo già incontrato, e
GetObjectName
, (OttieniNomeOggetto), le quali restituiscono, rispettivamente, il nome della finestra e quello dell’oggetto.

I Menu.

Qualche pagina addietro, quando avevamo fatto il collaudo del nostro sistema per il controllo delle finestre, dentro al Blocco Note ed al visualizzarsi del menu File, non si era attivata la funzione Evento che entrava in azione alla comparsa di nuove finestre. Questo è dovuto al fatto che, appunto, un menu non è una finestra, bensì una serie di oggetti, che hanno ciascuno un proprio nome, e quindi una
Proprietà
, e che svolgono un’azione quando premuti, e quindi un
Metodo.

Durante il normale utilizzo ci pensa Jaws, con le sue funzionalità, a leggerci i singoli nomi degli oggetti che compongono il menu. Se invece tali nomi li dobbiamo acquisire per elaborarli in uno script, potremo usare:

  1. GetObjectName ()
    , la funzione citata in precedenza, per leggerli uno alla volta.
  2. GetListOfObjects
    , (OttieniElencoDegliOggetti), che, come illustra la traduzione, raccoglie tutti gli oggetti presenti nell’elemento focalizzato, come ad esempio le voci di un menu.

Le Finestre Secondarie.

Un’altra tipologia frequente di elementi che incontriamo sono i controlli nelle finestre secondarie, per le quali facciamo subito un esempio. Dentro all’Editor di Script, se premiamo la scelta rapida
Control+H
per visualizzare la Finestra
Sostituisci
, dopo aver compilato i campi per la ricerca e la sostituzione, compariranno questi elementi, o controlli, di vario tipo:

  • Trova:
  • Sostituisci con:
  • Solo parole intere
  • Maiuscole/minuscole
  • Trova successivo
  • Sostituisci
  • Sostituisci tutto
  • Annulla
  • ?

In questo elenco ci sono campi di inserimento testo, caselle da selezionare, pulsanti da cliccare, ecc. Ebbene, tutti questi elementi sono sia delle ulteriori finestre dentro alla finestra Sostituisci, sia singoli oggetti. In questo caso, le funzioni da cui ottenere le informazioni possono essere quelle riguardanti le finestre o gli oggetti, a seconda dei casi, sulla base del tipo di dati necessario.

Di solito, nelle finestre secondarie, possono essere più precise le funzioni per gli oggetti. Ad esempio, nei campi attivabili con la barra spazio, che potrebbero essere
Solo parole intere
o
Maiuscole/minuscole
, se ci si trova sopra ad essi con il cursore, per sapere se il controllo è attivo oppure no, si possono usare due funzioni integrate:

  1. GetObjectState
    , (OttieniStatoOggetto), la quale restituisce la dicitura
    Attivato
    , oppure
    Non attivato
    , a seconda dello stato del controllo.
  2. GetObjectStateCode
    , (OttieniCodiceStatoOggetto), la quale fornisce un valore numerico, TRUE in caso di controllo attivato, oppure FALSE nel caso di controllo disattivo.

Tra le molte funzioni riguardanti gli oggetti, in quella che andremo a realizzare ora, utilizzeremo in particolare:

  1. GetObjectValue
    , (OttieniValoreOggetto), che serve per rilevare l’eventuale contenuto di un oggetto, come ad esempio il testo presente in un campo di editazione. La funzione avrebbe due parametri, che sono però opzionali e che quindi non serve specificare.
  2. GetObjectSubTypeCode
    , (OttieniCodiceSottotipoOggetto), la quale fornisce il codice che ci serve per capire su quale controllo si fermi il comando dell’applicativo dopo aver concluso la ricerca. Anche questa funzione avrebbe due parametri, ma anch’essi sono opzionali e quindi si possono omettere.

Ripetere l’ultima ricerca all’indietro.

Come esempio di utilizzo delle funzioni sugli oggetti, rimediamo ad una lacuna di uno script creato nello scorso capitolo , quello che ripete la ricerca precedente. Se ricordate, avevamo avvertito che quel tipo di script avrebbe funzionato solo nel Blocco Note in Windows 10, mentre non funzionava nella versione fornita con i sistemi operativi precedenti.

Ora, servendoci di una funzione che interroga gli oggetti, possiamo anche impostare la ricerca all’indietro, l’opzione che ci consente appunto di trovare l’occorrenza precedente dell’ultimo termine ricercato, controllando poi l’esito di tale ricerca.

Come preannunciato, cogliamo però l’occasione per fare un esempio anche di come trattare gli elementi di codice nei file esterni, creando la funzione che interroga gli oggetti dentro al file script collegato,
BloccoNote.JSS
. Questa dislocazione non è tuttavia casuale, o imposta solo da motivi didattici, ma è dovuta al fatto che anch’essa è destinata ad essere poi dislocata nel file script Predefinito.

Lo script che la richiama,
CercaRipetiPrecedente ()
, lo realizzeremo invece nella posizione più naturale, tornando al file script principale del Blocco Note, perché lì dovrà poi rimanere. Esso, infatti, rappresenta una necessaria versione locale, pressoché identica all’analogo script già realizzato nel file predefinito, e che doteremo più avanti di un’ulteriore possibilità.

Proprio per questa futura necessità, produrremo qui una versione locale anche dell’altro script,
CercaRipetiSuccessivo ()
, anche perché pressoché identico al primo script da creare.

A tutti gli utenti di Windows 10, è importante segnalare che tale predisposizione deve essere realizzata anche da loro, seppure come detto tale funzionalità da creare sia già disponibile nell’ultima versione del Blocco Note. Per questo, li invitiamo caldamente a seguire quanto proposto di seguito.

Esercizio 8.6.4. La funzione EsegueRicerca.

FileScript.

BloccoNote.JSS

Nome.

EsegueRicerca

Descrizione.

Ricerca del testo tramite la relativa funzione dell’applicativo.

Ritorni.

Di tipo Int. L’esito della ricerca: TRUE per la riuscita, FALSE per il fallimento.

Parametri.
  1. sTesto. Per Riferimento . Il testo da cercare. Di tipo String.
  2. sOggetto. Il tipo di ricerca da compiere. Di tipo String.
Novità.
  1. Le funzioni integrate
    GetObjectValue ()
    e
    GetObjectSubTypeCode ()
    , già presentate nella premessa.
  2. Una prima coppia di costanti,
    ALT_G
    e
    ALT_S
    ,le quali servono a simulare la pressione delle omologhe combinazioni tasti.
  3. Un’altra serie di costanti testuali,
    PRIMO,
    ULTIMO,
    PRECEDENTE,
    SUCCESSIVO
    , che corrispondono al tipo di ricerca da effettuare.
  4. L’ultima nostra costante,
    PULSANTE_RADIO
    , equivalente al valore 19, che indica il codice di sottotipo dell’omonimo tipo di controllo.
Fasi.
  1. Dopo aver spento la sintesi, si chiama la nostra funzione
    ComandoTrova ()
    , che fa comparire la finestra di ricerca dell’applicativo.
  2. Una prima struttura di controllo verifica se sia stato posto del testo come parametro, ed in questo caso lo inserisce. Altrimenti, il testo presente nel campo di ricerca viene salvato in una variabile che sarà poi trasmessa
    per riferimento.
  3. Dopo aver avviato la ricerca, un’ulteriore struttura verifica se il flusso è stato fermato dentro alla finestra di dialogo
    Trova
    , ed in tal caso viene simulata un’altra pressione di Escape per tonare al documento, registrando l’errore.
  4. All’uscita dalla serie di controlli, si riattiva la sintesi e, sulla base della variabile d’errore, un ultimo controllo restituisce l’esito positivo o un risultato nullo.
Note.
  1. Il primo parametro viene passato
    per riferimento
    perché, in caso di esito negativo della ricerca, lo script chiamante la funzione deve mostrare un messaggio nel quale è utile possa essere riportata la stringa di cui non si è trovata un ulteriore occorrenza.
  2. Come abbiamo già illustrato, il secondo parametro indica il tipo di ricerca da effettuare. Se il significato delle costanti è facilmente comprensibile, va soltanto chiarito che
    PRIMO
    e
    ULTIMO
    , che richiedono rispettivamente la prima e l’ultima occorrenza del termine ricercato, saranno usate più avanti, quando appronteremo un sistema per portarci alle etichette di inizio codice Personale.
  3. Anche in questo caso, così come in precedenza, utilizziamo l’istruzione con la funzione integrata
    Delay ()
    , stavolta per accertarsi che la lettura del tipo di controllo sia corretta, al termine della ricerca effettuata.
  4. Se ricordate, quando avevamo convertito dal precedente script all’attuale forma la funzione
    ComandoTrova ()
    , avevamo disabilitato lo spegnimento e la riattivazione della sintesi, proprio nella prospettiva del richiamo da dentro all’elemento che stiamo ora realizzando. In questo caso, infatti, tale tipo di azioni sono svolte alle fasi 1 e 4. Per questo, quando ci sono delle funzioni che sono destinate ad essere chiamate da altre, non si possono lasciare al loro interno dei comandi destinati ad agire direttamente sulla sintesi vocale. Nel prossimo capitolo, in particolare, analizzeremo meglio questa esigenza, realizzando una funzione che serve proprio a gestire questo aspetto legato allo spegnere e riattivare la voce di Jaws.

Codice.


Int Function EsegueRicerca (string ByRef sTesto, string sOggetto)
Var Int iErrore; esito della ricerca
SpeechOff (); spegne la sintesi
ComandoTrova (); attiva la finestra di ricerca testo
Pause ()
If sTesto Then; se un qualche testo è stato specificato,
TypeString (sTesto); lo immette nel campo di ricerca
Pause ();
Else; altrimenti,
Let sTesto = GetObjectValue (); rileva il contenuto del campo di ricerca
EndIf; fine controllo testo
; se si è richiesta la ricerca dall’inizio oppure in avanti,
If sOggetto == PRIMO || sOggetto == SUCCESSIVO Then
TypeKey (ALT_G); attiva la ricerca all’ingiù
Else; altrimenti,
TypeKey (ALT_S); attiva la ricerca all’insù
EndIf; fine controllo direzione indietro
EnterKey ();
EscapeKey ()
Delay (ATTESA); ritarda il flusso per i decimi di secondo impostati
If GetObjectSubTypeCode () == PULSANTE_RADIO Then; se si resta nella finestra,
EscapeKey (); ne esce,
Let iErrore = TRUE; e memorizza la conclusione anomala
EndIf; fine controllo esito
SpeechOn ();riattiva la sintesi
If !iErrore Then; se non si sono verificate anomalie,
Return TRUE; restituisce l’esito positivo
Else; altrimenti,
Return FALSE; restituisce un risultato nullo
EndIf; fine controllo esito
EndFunction

Collaudo.

Come spesso accade, si rinvia al primo script utile per collaudare la funzione appena creata. Per il momento, limitatevi a chiudere il file script esterno, tornando poi a quello principale del Blocco Note.

Esercizio 8.6.5. La versione di CercaRipetiPrecedente () per il Blocco Note.

FileScript.

Notepad.JSS

Nome.

CercaRipetiPrecedente

Sommario.

Ripete l’ultima ricerca all’indietro.

Descrizione.

Cerca la precedente occorrenza del testo inserito nell’ultima ricerca effettuata.

TastiAttivazione.

Nessuno

Novità.
  1. La nostra funzione
    EsegueRicerca ()
    , cui porremo come secondo parametro la costante
    PRECEDENTE
    , che contiene l’omonimo termine.
  2. La costante
    MB_OK
    , che indica il primo tipo di dialogo con
    ExMessageBox ()
    , il quale presenta un avviso a cui si può solo dare una conferma.
Fasi.
  1. Si inizia con l’ormai tradizionale controllo sulle finestre, a cui abbiamo però tolto la possibilità di attivare la fase Aiuto, e che pure stavolta abbiamo messo in negativo. Quindi, se non si è nella finestra corretta, il flusso entra nella struttura, chiama la nostra funzione per ripetere i tasti premuti, interrompendo poi definitivamente il flusso.
  2. Se il flusso prosegue, un secondo controllo è costituito dalla nostra funzione
    EsegueRicerca ()
    . Ad essa saranno indicati, nell’ordine, dapprima una variabile testuale, la quale restituirà per riferimento la stringa cercata, e poi la citata costante con il tipo di ricerca da effettuare.
  3. Se l’esito della funzione è negativo, e quindi la ricerca si è conclusa in modo anomalo, viene proposta la finestra di dialogo per la conferma tramite l’istruzione
    ExMessageBox ().
  4. In ogni caso, alla fine viene letta la riga corrente.
Note.
  1. Il nome scelto per lo script, nel formato a tre termini che avevamo già conosciuto con
    PronunciaValoreRiga ()
    , sarà spiegato a partire dal prossimo capitolo, utilizzando un nuovo modo per chiamare le funzioni.
  2. La dichiarazione della variabile
    sTesto
    , che serve per poter far pronunciare la stringa di cui non si sia trovata l’occorrenza, è stata posta dopo il controllo sulle finestre, perché prima di esso tale variabile non è coinvolta.
  3. Nell’istruzione
    ExMessageBox ()
    , il tipo di dialogo,
    MB_OK
    è stato indicato da solo come parametro, senza specificare alcuna altra icona o elemento separato tramite il carattere
    Pipe
    . Questo per evitare che sia emesso un ulteriore suono, oltre a quello già prodotto direttamente dall’applicativo al verificarsi dell’errore nella ricerca.

Codice.


Script CercaRipetiPrecedente ()
If !SiamoNellaFinestra (EDITING) Then; se non si è nella finestra corretta,
PronunciaEsegue (); ripete i tasti premuti,
Return; e interrompe il flusso
EndIf; fine controllo finestra
Var String sTesto; stringa da ricercare
If !EsegueRicerca (sTesto, PRECEDENTE) Then; se la ricerca fallisce,
ExMessageBox (NULLO, FormatString (msgNoTesto, sTesto), MB_OK); propone l’avviso
EndIf; fine controllo esito
SayLine ();in ogni caso, legge la riga corrente
EndScript

Collaudo.

Quando la compilazione va a buon fine, dovremo attendere un ultimo script, la versione locale anche per la ripetizione della ricerca in avanti. Infatti, quando noi impostiamo la ricerca all’indietro tramite questo script, per invertire nuovamente la direzione delle ricerche è necessario specificarlo espressamente, tramite appunto un elemento di codice a ciò dedicato.

Esercizio 8.6.6. La versione di CercaRipetiSuccessivo () per il Blocco Note.

FileScript.

Notepad.JSS

Nome.

CercaRipetiSuccessivo

Sommario.

Ripete l’ultima ricerca in avanti.

Descrizione.

Cerca la successiva occorrenza del testo inserito nell’ultima ricerca effettuata.

TastiAttivazione.

Nessuno

Note.
  1. Le fasi sono le stesse dello script precedente, e l’unica differenza sta nella costante
    SUCCESSIVO
    , posta come parametro alla nostra funzione
    EsegueRicerca ()
    . Per questo, non essendo poi da specificare alcun tasto di attivazione, si consiglia di copiare lo script precedente, avendo l’accortezza di sostituire i termini
    Successivo
    sia nel titolo, sia nella citata costante, prima di passare alla compilazione.
  2. In generale, Il fatto che due script si differenzino soltanto per una parola fa pensare che sarebbe più conveniente creare una funzione che svolga lo stesso compito, riducendo il codice degli script alla sola chiamata a tale elemento. Il prossimo capitolo, in particolare, troveremo proprio questo tipo di approccio nella costruzione dei nostri script.

Codice.


Script CercaRipetiSuccessivo ()
If !SiamoNellaFinestra (EDITING) Then; se non si è nella finestra corretta,
PronunciaEsegue (); ripete i tasti premuti,
Return; e interrompe il flusso
EndIf; fine controllo finestra
Var String sTesto; stringa da ricercare
If !EsegueRicerca (sTesto, SUCCESSIVO) Then; se la ricerca fallisce,
ExMessageBox (NULLO, FormatString (msgNoTesto, sTesto), MB_OK); propone l’avviso
EndIf; fine controllo esito
SayLine ();in ogni caso, legge la riga corrente
EndScript

Collaudo.

  1. Se riuscite a compilare senza problemi, possiamo finalmente dedicarci al collaudo della funzione di questa procedura, e degli script che la chiamano. Iniziate aprendo il Blocco Note, con un documento che abbia più occorrenze di un certo termine, e cercate tale stringa tramite il normale comando di ricerca.
  2. Effettuate almeno un paio di ripetizioni della ricerca, eseguendo subito lo script appena creato a livello locale, premendo
    Control+PaginaGiù.
  3. Richiamate ora il precedente script da testare, con
    Control+PaginaSu
    , ripetendo più volte il comando finché non si giunga alla prima occorrenza del termine all’inizio del documento.
  4. Arrivati a quel punto, dovrebbe proporsi la finestra di dialogo con l’avviso che il termine non è stato trovato, a cui si può dare solo Invio.
  5. La stessa cosa succederebbe se si ripetesse la ricerca all’occorrenza successiva. Più avanti, come detto, completeremo l’opera, producendo la forma definitiva di questa coppia di script, la quale consentirà anche di riprendere la ricerca dalla parte opposta del documento .

Elementi di codice nel file script principale ed in quello collegato.

Il collaudo degli script appena realizzati, se riuscito, ha dimostrato come il collegamento tra file script principale ed esterno si compia in entrambe le direzioni. Se qui ora, con uno script nel file principale , abbiamo chiamato una funzione posta nel file esterno, in precedenza lo script
TrovaTesto ()
spostato nel file esterno aveva richiamato la funzione
ComandoTrova ()
posizionata in quello principale.

In più, si può dire che gli elementi di codice presenti nel file script esterno, collegato a quello principale tramite l’istruzione
Use
, hanno la priorità su quelli presenti nel file script Predefinito. Ad esempio, se noi avessimo anche nel file script
Default.JSS
una funzione che si chiamasse
EsegueRicerca ()
, gli script
CercaRipetiPrecedente ()
, o
CercaRipetiSuccessivo ()
, chiamerebbero comunque la versione presente nel file script collegato, non quella del file predefinito.

***

L’aiuto in linea automatico.

Fin qui abbiamo realizzato una ventina di script nel file Predefinito, ed un altro paio dentro a Blocco Note e Microsoft Word. Può essere quindi assai probabile, specialmente all’inizio, aver bisogno di controllare quali siano i tasti di attivazione per quello script, o persino quali siano le funzioni disponibili tramite gli elementi di codice da noi creati.

Malgrado sia sempre possibile riprendere in mano queste pagine, appare utile poter disporre di un aiuto in linea che possa darci, per ciascuno script, i tasti di attivazione, le informazioni del suo sommario e, perché no, anche il nome. La cosa migliore, inoltre, è che tale aiuto in linea si possa aggiornare da solo, man mano che noi produciamo del nuovo codice, evitando così di doverne scrivere a mano, e di volta in volta, i contenuti.

L’aspetto importante sarà che l’Aiuto in Linea fornito da Jaws, attivabile con
Insert+H
, resterà lì a vostra disposizione, in quanto noi utilizzeremo altri tasti per richiamarlo. Ci serviremo tuttavia dello stesso sistema per proporlo, il
Visualizzatore Virtuale
, così come del resto abbiamo fatto nel caso delle informazioni sui dati nelle finestre.

Pur lasciando i dettagli alle schede dei singoli elementi, si può anticipare l’idea che sta alla base della procedura, che si sviluppa in questa serie di azioni:

  1. Si organizza la lettura dei file Tasti in due fasi, prima raccogliendo i dati da quello dell’applicativo corrente, poi il Predefinito, presenti nella cartella delle
    Impostazioni personali.
  2. In ciascuno dei due archivi elaborati si controlla che non vi siano ripetizioni tra i tasti di Attivazione , e script abbinati, nelle diverse sezioni degli archivi.
  3. Si inviano al
    Visualizzatore Virtuale
    le informazioni sugli script rilevati, verificando che non vi siano ripetizioni stavolta tra l’eventuale file dell’applicativo e quello Predefinito.

La procedura sarà così composta da due elementi di codice:

  • una funzione per realizzare l’obiettivo citato nel punto 2.
  • Lo script vero e proprio, che si occupa di gestire il tutto ed eseguire quello che manca, ovvero i punti 1 e 3.

Tali elementi di codice saranno preceduti da alcune pillole di teoria, che serviranno ad introdurre le principali novità nel lavoro da svolgere.

Elaborare gli elenchi.

Nel quinto capitolo abbiamo fatto conoscenza con degli elenchi di voci, separate in quel caso dal carattere
PIPE
. Quel tipo di struttura in informatica viene chiamata
Array
, (Vettore), ed è importante perché di solito presente in ogni linguaggio di programmazione.

Proprio perché queste pagine non sono, e non vogliono essere, un manuale informatico, nel resto della trattazione continueremo ad usare il termine
Elenco
per indicare queste strutture di dati. Era però importante dare questa informazione, anche per capire che non siamo molto lontani da quello che potrebbe essere un approccio molto più serio a tale tipo di argomenti.

In ogni caso, i dati vengono posti in queste speciali strutture per poterli utilizzare non solo così come sono, come una serie di termini affiancati, ma soprattutto per poterli elaborare. Finora, abbiamo già imparato come poter contare gli elementi di un elenco tramite la funzione
StringSegmentCount ()
, ed anche come estrarne uno in particolare, servendoci di
StringSegment ().

Adesso, ci serviremo dell’ultima funzione di Jaws che utilizza questo tipo di strutture,
StringSegmentIndex
, (IndiceDiSottostringaTestuale), che restituisce non più un dato, bensì il numero progressivo dell’elemento dove è casomai presente il dato che si è chiesto di cercare. Tale funzione ha quattro parametri:

  1. Il nome dell’elenco dei dati.
  2. Il carattere usato come separatore dei vari elementi della struttura.
  3. La stringa di cui cercare l’occorrenza tra i vari elementi, di cui devono corrispondere anche i caratteri maiuscoli o minuscoli.
  4. Un valore che, se positivo, indica che la stringa cercata deve coincidere esattamente anche come lunghezza. Se non si specifica nulla, o il valore FALSE, è sufficiente che la stringa cercata sia stata trovata all’interno di un singolo elemento dell’elenco.

Per fare un esempio, utilizziamo quanto proposto nel quinto capitolo, riproponendo quello che allora era soltanto una serie di saluti:


Buongiorno|Buon pomeriggio|Buonasera|Buonanotte

In quel caso, l’obiettivo era di estrarre l’elemento
"Buonasera"
, e per farlo avevamo specificato il valore
"3"
come terzo parametro alla funzione
StringSegment ()
. Stavolta, invece, facciamo finta di non conoscere il contenuto dell’elenco, e di voler sapere se quel tipo di elemento è presente , e casomai quale esso sia al suo interno.

Per ottenere questo, appunto, dovremo servirci della terza funzione citata. Ricordando che all’elenco avevamo dato il nome di
lstSaluti
, quella che presentiamo di seguito sarebbe l’istruzione necessaria a raggiungere il nostro scopo, che proponiamo con ciascun parametro su una riga diversa per facilitarne la comprensione:


Let iValore = StringSegmentIndex ( ; chiamo la funzione per la posizione della stringa
lstSaluti, ; parametro1, l’elenco dei dati
PIPE, ; parametro2, il separatore degli elementi dell’elenco
"Buonasera", ; la stringa da cercare nell’elenco
TRUE); il valore che indica di cercare una voce dell’elenco che coincida esattamente

Come potete notare, nel terzo parametro abbiamo posto l’elemento da cercare, mentre il quarto parametro valorizzato con
TRUE
serve ad indicare che la stringa specificata deve coincidere sia come caratteri, sia come lunghezza.

Così, il valore numerico che noi avremmo ottenuto con questa istruzione sarebbe ovviamente
3
, che quindi potremo utilizzare anche direttamente come terzo parametro in una funzione
StringSegment ()
per estrarlo dalla struttura.

Oltre a questo utilizzo in positivo, tale funzione può essere usata anche con l’operatore di
Negazione
davanti, il Punto Esclamativo, per verificare che un dato al contrario non sia presente nell’elenco esaminato. Per questo, riprendendo l’esempio precedente, ora si potrebbe usare questa diversa forma, con il Punto Esclamativo davanti all’istruzione:


If !StringSegmentIndex (lstSaluto, PIPE; "Buonasera", TRUE) Then

Se questa forma restituisse un risultato positivo, vorrebbe dire che il dato cercato nell’elenco non c’è, e che quindi tale dato sarebbe casomai da aggiungere alla struttura di dati, o in generale che il flusso può continuare all’interno del codice.

Questo, in particolare, sarà proprio il tipo di utilizzo che noi faremo della funzione, sia nel primo elemento di codice, sia nello script generale.

Leggere i dati da determinate cartelle.

Quando noi aggiorniamo o creiamo uno script, come detto, il nostro lavoro viene salvato di norma nella cartella delle
Impostazioni personali
. Per rilevare un elenco completo di tali script, quindi, dovremo andare a leggere proprio dentro a quella cartella, accertandoci inoltre che quella sia l’unica in cui tali dati siano cercati.

Per ottenere questo risultato, dovremo servirci di una speciale serie di funzioni di lettura dati, le quali si differenziano dalle altre per un semplice suffisso,
Ex
, (Ulteriore). Tali funzioni, infatti, consentono di specificare un particolare valore come parametro, che indica a Jaws dove andare a cercare gli archivi desiderati.

La prima di queste che useremo è la funzione integrata
IniReadSectionKeysEx
, (LeggeChiaviDiSezioneNeiFileIniUlteriore), molto simile all’omologa funzione senza suffisso. La differenza tra le due versioni è solo un parametro in più, che dagli originari due passano quindi ai seguenti tre:

  1. La sezione in cui cercare i dati.
  2. Un codice numerico che indica in quale cartella cercare il file da cui leggere i dati.
  3. Il nome di questo file archivio.

Nel nuovo parametro, il secondo, il codice da inserire è uno dei valori impostati tramite le costanti del file Predefinito,
HJConst.JSH
. Nel nostro caso, useremo quello della costante
FLOC_USER_SETTINGS
,(CollocazioneFileImpostazioniUtente), dal valore 6, che per comodità convertiremo comunque in una costante personalizzata dal nome in italiano.

L’altra funzione integrata di cui ci serviremo è
IniReadStringEx
, (LeggiDatoTestualeFileIniUlteriore), che come la versione senza suffisso serve a leggere i singoli dati dagli archivi. Anche in questo caso, rispetto alla versione precedente, tale funzione ha in più solo quel citato parametro, portando quindi il totale ai seguenti cinque:

  1. La sezione del file INI da trattare.
  2. La chiave del dato da elaborare.
  3. Un valore da restituire, qualora il dato sia vuoto.
  4. Un codice numerico che indica in quale cartella cercare il file.
  5. Il nome del File Tasti da cui leggere, espresso anche senza il percorso completo.

Come avete potuto notare, il nuovo arrivo è il quarto, dove si dovrà inserire il valore citato in precedenza, che nel codice assumerà l’aspetto di una nostra costante dal valore 6.

Esercizio 8.7.3. La funzione ComprimeDati.

FileScript.

Default.JSS

Nome.

ComprimeDati

Descrizione.

Raccoglie i dati di un’archivio, prelevandoli dalle sezioni specificate, evitando che vi siano delle ripetizioni.

Ritorni.

Di tipo Void. Nessuno.

Parametri.
  1. sFile. Il nome dell’archivio in cui leggere i dati. Di tipo String.
  2. sSezioni. L’elenco delle sezioni dell’archivio in cui rilevare i dati. Di tipo String.
  3. sChiavi. Per Riferimento. L’elenco delle chiavi dei dati. Di tipo String.
  4. sDati. Per Riferimento. L’elenco dei dati abbinati alle chiavi. Di tipo String.
Novità.
  1. Le funzioni integrate
    StringSegmentIndex (),
    IniReadSectionKeysEx ()
    e
    IniReadStringEx ()
    , già presentate nelle premesse tecniche.
  2. La costante
    UTENTE
    , che equivale al valore 6, e che indica a Jaws di cercare i file archivio nella cartella delle
    Impostazioni personali.
Fasi.
  1. Si avvia un ciclo che scorre le sezioni inserite nell’elenco specificato come parametro, leggendo le chiavi delle singole sezioni tramite la funzione integrata
    IniReadSectionKeysEx ().
  2. Viene avviato anche un secondo ciclo, che scorre le singole chiavi di ciascuna sezione. Tali chiavi sono aggiunte ad un elenco da trasmettere solo se non sono presenti, controllandone il contenuto tramite la funzione integrata
    StringSegmentIndex ().
  3. Se una chiave non è già stata registrata, viene aggiunta all’elenco e ne viene letto anche il dato abbinato, tramite la funzione integrata
    IniReadStringEx ().
  4. Alla conclusione di entrambi i cicli, gli eventuali elenchi rilevati sono trasmessi allo script chiamante
    per riferimento.
Note.
  1. Le sezioni in cui cercare i dati sono le tre principali, citate anche nello scorso capitolo,
    Common Keys,
    Desktop Keys
    e
    Laptop Keys
    . Se si avesse la necessità di aggiungerne altre, come ad esempio quelle riservate ai comandi per la
    Barra Braille
    , basterà rilevare il nome esatto della sezione desiderata all’interno del file
    JKM
    , relativo al singolo applicativo o al file script Predefinito, aggiungendolo poi alle citate voci già presenti nel file
    _PsMessages.JSM
    , al termine della riga sottostante al seguente indicatore di messaggio:
  2. @lstSezioniTasti

Codice.


Void Function ComprimeDati (string sFile, string sSezioni, ; prima riga con due parametri,
string ByRef sChiavi, string ByRef sDati); e seconda riga, con gli altri due
Var
Int j, ; contatore del ciclo primario
Int k, ; contatore del secondo ciclo
String sSezione, ; sezione del file da leggere
String sElenco, ; elenco temporaneo delle chiavi
String sChiave, ; chiave del dato
String sDato; singolo dato da leggere
For j = 1 To StringSegmentCount (sSezioni, PIPE); scorre le sezioni da leggere
Let sSezione = StringSegment (sSezioni, PIPE, j); estrae una singola sezione
Let sElenco = IniReadSectionKeysEx (sSezione, UTENTE, sFile); rileva le chiavi dei dati
For k = 1 To StringSegmentCount (sElenco,PIPE); scorre l’elenco appena rilevato
Let sChiave = StringSegment (sElenco, PIPE, k); ne estrae la singola chiave
Let sDato = IniReadStringEx (sSezione, sChiave, NULLO, UTENTE, sFile); rileva il dato
if !StringSegmentIndex (sChiavi, PIPE, sChiave, TRUE) Then; se la chiave non c’è,
If (j + k) >2 Then; se non si è al primo passaggio,
Let sChiavi = FormatString (msg2Pipe, sChiavi, sChiave); la aggiunge al primo elenco,
Let sDati = FormatString (msg2Pipe, sDati, sDato); e aggiunge anche il dato rilevato
Else; altrimenti,
Let sChiavi = sChiave; inizializza il primo elenco,
Let sDati = sDato; ed imposta anche il secondo
EndIf; fine controllo passaggi
EndIf; fine controllo chiavi registrate
EndFor; fine scorrimento chiavi
EndFor; fine scorrimento sezioni
EndFunction

Collaudo.

  1. Se la compilazione riesce, rinviamo il collaudo al termine del prossimo elemento di codice.
  2. Prima di questo, tuttavia, dobbiamo aprire un’altra parentesi informativa per introdurre un nuovo argomento, che ci porterà poi sino allo script principale della procedura.

Utilizzo del Visualizzatore Virtuale.

Il
Visualizzatore Virtuale
, che abbiamo già incontrato in queste pagine, è l’ambiente dove si può proporre del testo che può essere esplorato tramite i consueti tasti di movimento. Proprio per questo, dentro ad esso sono proposte le schermate della Guida di Jaws, oppure la
Cronologia della Sintesi
, ed anche noi nel sesto capitolo l’avevamo usato per visualizzare i dati sulle finestre.

Se ricordate, in quell’occasione ci eravamo serviti del metodo più semplice per inviarvi del testo, usando la funzione
SayMessage ()
, cui abbiamo specificato l’apposito valore come
Tipo di Output
. Questo tipo di approccio era però quello classico, che utilizziamo di solito nei nostri script, il quale prevede nella sostanza solo due fasi:

  1. Raccolta ed elaborazione dei dati.
  2. Invio all’Utente tramite una funzione di lettura.

In realtà, il
Visualizzatore Virtuale
offre molte più possibilità, come stanno a dimostrare il gran numero di funzioni che lo riguardano. Tra l’altro, tali funzioni sono facilmente riconoscibili per avere ad inizio nome, o qualche volta anche al suo interno, le parole
UserBuffer
, (BufferUtente), una coppia di termini che d’ora in poi potremo anche usare per identificarlo.

Il cambio di logica prevede di considerare questo ambiente come un livello a sé stante, parallelo alle normali finestre degli Applicativi o del Sistema Operativo. Noi, quindi, possiamo inviare del testo al
BufferUtente
, magari anche nel corso di momenti diversi, poi decidere di portarlo a tutto schermo, nasconderlo, contarne, copiarne o cancellarne il contenuto, facendolo sia leggere dalla sintesi, sia apparire nello schermo in modo silenzioso.

Lo script principale della procedura, che deve proporre un
aiuto in linea
per tutti gli script da noi prodotti, coinvolge soltanto alcune delle molte funzioni disponibili. Inizieremo ad analizzarle partendo da
UserBufferAddText
, (AggiungiTestoBufferUtente), che si occupa appunto di aggiornare l’ambiente virtuale.

Tale funzione ha sei parametri, di cui però solo il primo è obbligatorio. Per quanto riguarda gli altri, gli ultimi tre consentono di impostare font, dimensioni ed attributi del testo a video, e quindi si può evitare di specificarli.

Pertanto, la nostra attenzione andrà rivolta solo ai seguenti primi tre:

  1. Il testo da inviare al
    BufferUtente.
  2. Il comando che si vuole venga eseguito quando si clicca sul testo presente nel primo parametro.
  3. La voce che si vuole appaia nell’
    Elenco dei link
    , visualizzabile con
    Insert+F7
    , che attiverebbe il comando inserito nel secondo parametro.

Poiché come detto solo il primo è obbligatorio, si può usare la funzione anche solo per scrivere il testo nel
BufferUtente
, senza specificare altri parametri.

Se però si indica anche il comando da eseguire cliccando sul testo, si dovrà specificarlo, così come nel nostro caso, con la parola chiave
"script"
, tutta in caratteri minuscoli, seguita da uno spazio e dal nome dello script da eseguire, concludendo poi con la classica coppia di parentesi al cui interno andrebbero casomai specificati eventuali parametri, come nell’esempio seguente:


script MioCodice ()

Il solo fatto di specificare un secondo parametro, attiva già la possibilità di cliccare sul testo per far eseguire il comando. Bisogna però indicare anche il terzo per far sì che esso assuma l’aspetto di un
link
, potendo così essere riconosciuto come tale dalla sintesi quando ci si passa sopra, ed inserendo il contenuto del parametro nel citato
Elenco dei link.

Delle altre quattro funzioni da noi utilizzate nel prossimo codice, proponiamo in sequenza le principali informazioni:

  1. EnsureNoUserBufferActive
    , (AccertaBufferUtenteNonAttivo), che disattiva la visualizzazione dell’ambiente virtuale. Possiede un parametro, dove se si inserisce
    TRUE
    si mantiene il testo presente, mentre se non si pone nulla o il valore
    FALSE
    il testo viene cancellato.
  2. UserBufferGetLength
    , (OttieniLunghezzaBufferUtente), che restituisce il numero di caratteri presenti nell’ambiente virtuale. Senza parametri.
  3. UserBufferActivate
    , (AttivaBufferUtente), che mostra a schermo l’ambiente virtuale. Avrebbe un parametro, che se posto a
    TRUE
    non fa passare i tasti premuti, ma che comunque di norma non va specificato.
  4. RedirectToUserBuffer
    , (ReindirizzaAlBufferUtente), che invia il testo specificato come unico parametro all’ambiente virtuale, facendo poi eseguire un comando
    SayAll
    , (LeggiTutto), che fa pronunciare anche il resto del testo eventualmente già presente nel
    BufferUtente.

Esercizio 8.7.5. Lo script AiutoInLinea.

FileScript.

Default.JSS

Nome.

AiutoInLinea

Sommario.

Elenca gli script Personali.

Descrizione.

Propone una schermata di informazioni sugli script per l’Utente che non sono presenti tra quelli Predefiniti.

TastiAttivazione.

Windows+H

Novità.
  1. Le funzioni integrate
    EnsureNoUserBufferActive (),
    UserBufferGetLength (),
    UserBufferAddText (),
    UserBufferActivate ()
    e
    RedirectToUserBuffer ()
    , presentate nella premessa.
  2. La nostra funzione
    ComprimeDati ().
  3. L’altra funzione integrata
    GetScriptSynopsis
    , (OttieniSommarioScript), che restituisce il
    Sommario
    dello script di cui si specifica il nome come primo parametro. La funzione avrebbe anche un secondo parametro opzionale, ma di norma si può fare a meno di specificarlo.
  4. La costante
    PUNTO
    , che equivale al carattere omonimo.
Fasi.
  1. Dopo aver resettato il
    BufferUtente
    tramite la funzione integrata
    EnsureNoUserBufferActive ()
    , si avvia un ciclo che scorre i due tipi di file Tasti, quello per l’Applicativo corrente e quello Predefinito.
  2. Dapprima si imposta il nome del file Tasti da elaborare, poi si crea un elenco delle combinazioni tasti da esso rilevate, e si effettuano le impostazioni relative alla fase del ciclo, servendosi anche di
    UserBufferGetLength ()
    e
    UserBufferAddText ().
  3. Viene quindi avviato un secondo ciclo, interno al precedente, che scorre le combinazioni tasti presenti nell’elenco, effettuando dei controlli per capire se elaborare la singola combinazione estratta.
  4. Se le condizioni sono positive, ne viene formattato ed inviato il testo informativo, comprensivo di
    link
    , tramite ancora
    UserBufferAddText ().
  5. Al termine dei due cicli, qualora nel
    BufferUtente
    vi sia del testo, ne viene attivata la visualizzazione tramite
    UserBufferActivate ()
    . Infine, si aggiunge la formula di chiusura della schermata, avviandone la lettura complessiva, con
    RedirectToUserBuffer ().
Note.
  1. Le informazioni relative a ciascuno script inizieranno dal
    Sommario
    , rilevato tramite
    GetScriptSynopsis ()
    , oppure, se in quest’ultimo non vi fosse nulla, dal nome dello script. In ogni caso, ciascuna riga di dati si concluderà con i tasti di attivazione, sotto forma di
    link
    così come sono visualizzate le analoghe informazioni anche nell’Aiuto in Linea di Jaws.
  2. L’elenco degli script, visualizzabile con
    Insert+F7
    , proporrà invece i soli nomi degli script, che si potranno avviare cliccando con Invio.
  3. La combinazione consigliata per eseguire lo script,
    Windows+H
    , è quella più semplice da ricordare, anche in un certo senso analoga a quella ufficiale dell’Aiuto di Jaws,
    Insert+H
    . Si tratta tuttavia di una scelta rapida quasi certamente già utilizzata, non tanto da Jaws stesso,quanto dal Sistema Operativo in sé. Se quindi foste abituati a servirvene già per altri scopi, ovviamente, sostituitela pure con una a vostra scelta.

Codice.


Script AiutoInLinea ()
Var
Int j, ; contatore del ciclo primario
Int k, ; contatore del secondo ciclo
String sFile, ; file tasti da elaborare
String sTitolo, ; titolo per inizio informazioni
String sPrimi, ; elenco iniziale dei tasti
String sChiavi, ; elenco combinazioni tasti rilevate
String sDati, ; nomi di script abbinati ai tasti
String sTasto, ; singola combinazione tasti che funge da chiave
String sNome, ; singolo script abbinato
String sInfo; informazioni da visualizzare
EnsureNoUserBufferActive (); chiude eventuali schermate del Visualizzatore Virtuale
For j = APP To JAWS; scorre i tipi di file tasti
Let sFile = NomeArchivio (j); imposta il nome del file tasti da elaborare
; crea gli elenchi leggendo dalle sezioni specificate dell’archivio
ComprimeDati (sFile, lstSezioniTasti, sChiavi, sDati)
if sChiavi Then; se dei tasti sono stati rilevati,
if j == APP Then; se si è al primo passaggio,
; formatta il titolo dell’Aiuto con il nome dell’applicativo corrente
Let sTitolo = FormatString (hlpInfoApp, GetActiveConfiguration ())
Let sPrimi = sChiavi; registra il primo elenco di tasti
ElIf j == JAWS Then; se si è invece al secondo passaggio,
Let sTitolo = hlpInfoJaws; imposta il relativo titolo per l’Aiuto
if UserBufferGetLength ()Then; se nel Visualizzatore Virtuale è presente del testo,
UserBufferAddText (FormatString (msgTraVuote, sTitolo)); inizializza la seconda parte
EndIf; fine controllo testo Aiuto
EndIf; fine controllo passaggi
For k = 1 To StringSegmentCount (sChiavi, PIPE); scorre le combinazioni tasti rilevate
Let sTasto = StringSegment (sChiavi, PIPE, k); estrae la combinazione tasti da verificare
if j == APP ; se si è al primo passaggio,
|| !StringSegmentIndex (sPrimi, PIPE, sTasto, TRUE) Then; o, se i tasti non ci sono già,
if !UserBufferGetLength ()Then; se il Visualizzatore Virtuale è è vuoto,
UserBufferAddText (sTitolo + LF); lo inizializza con l’apposito titolo
EndIf; fine controllo creazione testo
if sTasto != GetCurrentScriptKeyName () Then; se i tasti non sono quelli di attivazione,
Let sNome = StringSegment (sDati, PIPE, k); estrae il nome dello script abbinato
Let sInfo = GetScriptSynopsis (sNome); legge il sommario dello script
If !sInfo Then; se nessuna informazione è stata rilevata,
Let sInfo = sNome + PUNTO; usa come informazione il nome dello script
EndIf; fine controllo presenza informazioni
UserBufferAddText ( ; compone la riga d’informazioni, comprensiva del link,
FormatString (hlpInfoScript, sInfo, sTasto), ; formatta il testo da visualizzare,
FormatString (hlpInfoLink, sNome), ; formatta anche il comando da eseguire,
sNome); e aggiunge il nome per l’elenco dei link
EndIf; fine controllo tasti validi
EndIf; fine controllo tasti già elaborati
EndFor; fine ciclo scorrimento chiavi
EndIf; fine controllo presenza chiavi
EndFor; fine ciclo scorrimento file
if UserBufferGetLength ()Then; se nel Visualizzatore Virtuale è presente del testo,
UserBufferActivate (); visualizza la schermata d’informazioni,
; quindi, prima formatta un testo con i tasti dello script chiamante e la formula di chiusura,
Let sInfo = FormatString (hlpUltimaInfo, GetCurrentScriptKeyName (), hlpFineInfo);
; poi, la trasmette al Buffer, avviandone la lettura.
RedirectToUserBuffer (sInfo)
EndIf; fine controllo presenza testo
EndScript

Collaudo.

  1. Lo script è piuttosto complesso, e quindi non è improbabile qualche errore anche solo di battitura. Quando comunque riuscite a compilarlo con successo, premete il tasto di Attivazione da voi impostato ,
    Windows+H
    , o qualunque altro esso sia.
  2. Se siete nell’Editor di Script, il titolo nella prima riga in alto,
    "Tasti di utilizzo generale."
    , sta ad indicare che in questo caso gli script per l’Applicativo non ci sono, in quanto valgono soltanto quelli registrati nel file script Predefinito. In ogni caso, si è liberi di muoversi nella schermata, andando sopra le singole voci che sono anche dei link da poter cliccare per eseguire i comandi descritti. Per questo motivo, premendo
    Insert+F7
    , si otterrà il classico elenco verticale dei link, dove però i vari comandi saranno identificati dal nome dello script, non dalla sua descrizione. Anche qui, come succede di solito, premendo Invio si attiva il link, e quindi si esegue il comando correlato.
  3. Anche dentro a Wordpad, se avete seguito i passi consigliati, l’attivazione dell’
    aiuto in linea
    dovrebbe avervi proposto subito lo stesso titolo di carattere generale , perché gli script da noi realizzati in quell’applicativo utilizzano dei tasti già registrati per gli stessi script nel file Predefinito.
  4. Se invece aprite Microsoft Word o il Blocco Note, la prima riga in alto della schermata contiene la dicitura
    "Tasti Personali dentro a ..."
    , con la parte finale che propone il nome dell’applicativo corrente. Dopo un elenco di questi tasti validi per l’ambiente specifico, ci saranno comunque gli altri tasti generali, separati dai precedenti tramite lo stesso titolo di prima.

***

Riepilogo.

Se siete arrivati sin qui, potete essere davvero soddisfatti: con le lezioni, almeno quelle fondamentali, abbiamo finito!

Gli argomenti trattati in questo capitolo, in particolare, erano spesso ostici, e si spera di essere riusciti a spiegarci in modo sufficientemente chiaro ed esaustivo.

A partire dal prossimo Capitolo, saranno proposte solo applicazioni pratiche di quello che abbiamo sin qui imparato, mettendo a punto altre procedure, o veri e propri sistemi, che speriamo possano esservi utili.

In ogni caso, il solo essere costretti a lavorarci, cercando di capire cosa si stia facendo, può darvi lo spunto per altri personali script. Lo scopo, per tutti, è rendere sempre più accessibili i programmi che girano nel vostro PC.

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 *