Gli script di Jaws 13. Le procedure guidate: controllo dei dati e funzioni hook.

Programmiamoli da Soli!

Realizzare un’applicazione con gli script.

Dopo aver fatto tanta strada nel nostro viaggio, eccoci dunque arrivati all’ultimo argomento tra quelli trattati. In realtà, malgrado si tratti del rettilineo finale, la sua lunghezza consiglia di spezzarlo comunque in due parti, assegnando al capitolo che inizia un ruolo soltanto preparatorio.

Anche questo tema, per quanto complesso, ruota attorno ad un solo elemento di codice principale, che ci consente di far funzionare poi tutto quello che ci serve per raggiungere l’obiettivo. Nonostante tale elemento vada realizzato solo nel prossimo ed ultimo capitolo, è necessario chiarire fin d’ora che il nostro obiettivo è allestire una serie di procedure guidate, con le quali riusciremo in particolare a:

  1. Sfruttare le tre funzionalità mancanti di
    MuoveDato ()
    , potendo inoltre inserire e modificare le varie stringhe da utilizzare come elementi di ricerca, ed in più facendolo da dentro i tipi di documento a cui esse andranno applicate.
  2. Registrare e gestire le Macro testuali, sempre dai documenti dove andranno inserite.
  3. Scrivere, modificare, cancellare o duplicare gli script a gestione personale, direttamente da dentro gli applicativi in cui agiranno.

Così com’è successo lo scorso capitolo, sezioneremo la trattazione sulla base dei blocchi di elementi collegati tra essi, e che ruotano attorno ad una delle funzioni chiamate dal citato elemento principale. L’unica eccezione in tal senso sarà rappresentata dal modo in cui Jaws consente di cambiare la risposta ai tasti premuti, come avviene ad esempio nella modalità
"Aiuto tastiera"
, che illustreremo con più titoli e con degli elementi di codice per fare una prova pratica.

A parte le presentazioni dei singoli blocchi, ci fermeremo comunque di tanto in tanto per proporre delle brevi pillole di approfondimento di alcuni temi. Il collaudo del nostro lavoro, tuttavia, non potrà che essere rinviato all’ultimo capitolo.

Ed ora, per la penultima volta, il solito inizio dedicato ai nostri file personali.

Esercizio 13.1.1. Aggiornare il file Personale delle Costanti.


TUTTI = 2, ; valore per indicare l’aggiornamento di tutti i file tasti degli script a gestione personale
CANCELLA = 1, ; valore positivo per chiedere la cancellazione di un elemento
TASTI_HOOK = "TastiHook", ; funzione per predisporre la cattura dei tasti
FRECCIA_DESTRA = "RightArrow", ; versione inglese dell’omonimo tasto
FRECCIA_SINISTRA = "LeftArrow", ; versione inglese dell’omonimo tasto
GESTIONE = 3, ; indica gli script a gestione personale
NATIVO = 1, ; indica gli script nativi di Jaws
ERRORE2 = "DoDoop9", ; suono per errore in basso
INSERT_T = "Insert+T", ; combinazione tasti omonima
JAWSKEY_T = "JAWSKey+T", ; combinazione tasti omonima
PRE_PROCESS_KEY = "InterrompeAttesa (strKeyName); chiama l’apposita funzione",
MAX_PULSANTI = 5, ; numero massimo dei pulsanti nelle procedure guidate
ATTIVA_SINTESI = "AttivaSintesi", ; funzione omologa
SU = "su", ; termine omonimo

Esercizio 13.1.2. Aggiornare il file Personale delle Variabili Globali.


Int gnAggiorna, ; valore per la conclusione di un ciclo
Int gnConta, ; contatore temporaneo
Int gnEcoTastiera, ; valore corrente dell’omonima impostazione
Int gnLivello, ; ultimo livello elaborato in una procedura guidata
Int gnNuovoScript, ; indicatore di uno script di cui controllare la presenza nell’archivio personale
Int gnScriveTasto, ; attiva la trascrizione dei tasti premuti dentro a PreProcessKeyPressedEvent ()
int gnTasto, ; valore assunto dai tasti opzionali
int gnVoce, ; puntatore della voce selezionata
String gsAmbito, ; contesto in cui uno script a gestione personale deve agire
String gsAvviso, ; messaggio da leggere in modo differito
string gsChiama, ; prima parte del nome della funzione da chiamare alla conclusione della procedura
String gsInfo, ; informazioni nei campi di elaborazione testo.
String gsMassimi, ; elenco di valori massimi
String gsMinimi, ; elenco di valori minimi
String gsNuoviTasti, ; elenco dei tasti modificati durante la sessione
String gsSezione, ; il nome della sezione in cui operare
String gsSuffisso, ; ultimo suffisso specificato come parametro
String gsTastiScript, ; tasti di attivazione di uno script
String gsVoce, ; memorizza la voce corrente in un elenco
String gsRimuoviTasti, ; combinazione tasti di cui rimuovere l’assegnazione nel file tasti corrente
Int gnCambiati, ; indicatore della presenza di dati modificati nel record corrente

Esercizio 13.1.3. Aggiornare il file Personale dei Messaggi.


; Nessun file configurazione del tipo specificato
@msgNoFileCampi
File configurazione per la categoria %1 non trovato. Crearlo ora?
@@
; Nessuna fase valida presente nel file configurazione
@msgNoCampiValidi
Dati sui campi non presenti, o non validi, nel file %1.JCF. Crearli ora?
@@
; invita a specificare i tasti di attivazione negli script a gestione personale
@msgAttivazione
Premere i tasti di attivazione dello script %1. Invio conferma, Esc annulla.
@@
; elenco prefissi direzionali per i tasti di attivazione
@lstPrefissiTasti
Left|Right
@@
; titolo per conferma alla sovrascrittura dei tasti di attivazione
@ttlTastiUtilizzati
I tasti premuti sono già utilizzati da uno script %1, %2.
@@
; elenco con i tipi di script
@lstTipiScript
nativo|dell’Utente|a gestione personale
@@
; mancata immissione di tasti per l’attivazione degli script a gestione personale
@ttlNoPremuti
Nessun tasto di attivazione indicato.
@@
; conferma al ritorno ad una fase d’inserimento
@msgTornaImmissione
Volete tornare all’inserimento?
@@
; conferma dell’abbandono di una fase d’inserimento
@ttlAbbandono
Confermate l’abbandono dell’inserimento?
@@
; avviso di modificare i tasti di attivazione script impostati
@msgCambiaTasti
Modificare i tasti premuti, o cambiare prima quelli dello script esistente.
@@
; messaggio per conferma alla sovrascrittura dei tasti di attivazione
@msgTastiUtilizzati
Continuando, si sovrascriverà l’impostazione esistente.
@@
; esito con registrazione effettuata
@msgOkTasti
Tasti memorizzati.
@@
; base per etichetta dei campi
@msgCampo
Campo%1
@@
; suffisso per le sezioni
@msgSezioni
nella sezione %1 dell’archivio %2.
@@
; base per nome delle chiavi con i dati sui pulsanti
@msgPulsante
Pulsante%1
@@
; base per aggiunta con il carattere Pipe
@msgPipe
|%1
@@
; base per un prefisso numerico
@msgNumerico
%1. %9
@@
; base per tre termini separati dal carattere Pipe
@msg3Pipe
%1|%2|%3
@@
; elenco delle opzioni impostabili come ambiti negli script personali
@lstAmbitiScript
Chiude|Esce|Salva|Tabella
@@
; virgola, e spazio successivo, aggiunti ad un termine
@msgVirgola
%1, %9
@@
; dati incompleti nei record
@ttlDatiMancanti
%1 record, %2con dati incompleti.
@@
; consenso alla rimozione dei record non validi
@msgRimozioneDati
Continuando, tali record saranno rimossi.
@@
; esito record cancellati
@hlpRimuoviRecord
%1 record rimoss%2.
@@
; errore nel trattamento dei record
@hlpErroreRecord
Impossibile %1re %2 record %3 %4.
@@

***

Dati preliminari.

Questo primo blocco di cinque funzioni è dedicato, come dice il titolo, ad impostare e controllare i dati preliminari. Per poter capire cosa siano questi dati, e quale sia il motivo di tale controllo, è però necessario spiegare in breve come funziona la procedura che stiamo allestendo.

Tra gli aspetti fin qui esaminati di Jaws, ciò che più si avvicina a quello che andremo a fare è la procedura guidata di creazione degli script. In altre parole, tramite un comando, noi entreremo in un ambiente nel quale potremo aggiungere,modificare o cancellare dei dati, i quali potranno essere a loro volta composti da:

  1. un solo campo, con un elenco di voci che potranno essere selezionate usando le frecce in su ed in giù, e che potranno essere modificate, aggiunte o eliminate, sempre e solo da questo elenco.
  2. Una serie di campi, dove ogni singola voce potrà essere come prima selezionata, modificata, aggiunta o eliminata, ma alla quale saranno collegati altri campi di un record, i quali potranno essere a loro volta elaborati, ciascuno in una propria e diversa schermata della stessa procedura.

Facendo un esempio con quel poco che già conosciamo, al primo tipo di procedura appartiene la ricerca di stringhe predeterminate, che abbiamo chiamato anche elementi di ricerca, dove all’ingresso nella procedura potremo inserirne un numero da uno a nove. Tali elementi, come detto, potranno essere così modificati o cancellati, aggiungendone poi fino al numero massimo impostato.

All’altro tipo di procedura appartengono invece gli script a gestione personale, che invece abbiamo stabilito che possano essere fino ad un massimo di 99. In questo caso, nella prima schermata l’elenco di voci sarà il nome da assegnare a tali script, mentre aprendo schermate successive, si potranno aggiungere o modificare i dati relativi ad altri campi, come i tasti di attivazione, il testo del sommario, o i comandi che ciascun script deve eseguire.

L’elemento comune a queste due tipologie di procedura è, come detto, la schermata principale, a cui anche negli script bisognerà tornare per effettuare le modifiche sostanziali, come l’aggiunta o la cancellazione. Pur partendo dunque da alcune basi comuni, il fatto di poter elaborare questa o quella tipologia di procedura dipende da alcuni dati, da noi predisposti fin dal capitolo scorso in alcuni file di configurazione in formato
"INI".

L’unico problema dei dati registrati in questi archivi testuali è che ciascun utente può, almeno in teoria, modificare tali archivi anche manualmente. Se tale opportunità è comunque utile, è pur sempre possibile modificare in modo errato tali file, qualora li si abbia per qualche motivo da aprire, casomai togliendo eventuali dati indispensabili al funzionamento delle singole procedure.

Per questo motivo, in particolare una delle prime funzioni che andremo a realizzare nel prossimo blocco si occupa proprio di controllare il contenuto dei file di configurazione, quelli che lo scorso capitolo abbiamo previsto di creare tramite un apposito elemento di codice. Se le sezioni indispensabili dell’archivio sono presenti, a prescindere dall’ordine in cui si trovino, allora il flusso della procedura prosegue, altrimenti viene fermato.

Anche se l’elemento chiamato dalla funzione principale è l’ultimo del blocco, la funzione più complessa è però quella che controlla l’eventuale corretta presenza di tasti di attivazione negli script, in particolare di quelli a gestione personale. Tale funzione, lunga un centinaio di righe, controlla gli abbinamenti tra tasti e script, verificando che essi siano corretti, e casomai cancellandoli, qualora lo script abbinato non esista.

La funzione, in queste fasi iniziali, serve ad evitare che siano elaborati dei dati inesatti. Questo stesso elemento, in ogni caso, sarà chiamato anche alla fine della procedura, per aggiornare i file tasti con le eventuali modifiche apportate alle impostazioni registrate.

Esercizio 13.2.1. La funzione ResettaCampi.

FileScript.

Default.JSS

Nome.

ResettaCampi

Descrizione.

Azzera valori e dati delle procedure guidate.

Ritorni.

Di tipo Void. Nessuno.

Note.
  1. Questa funzione viene chiamata sia all’inizio, per non causare effetti indesiderati durante le impostazioni, sia alla fine del lavoro, per lasciare la memoria pulita, e consentire l’utilizzo di tali variabili per altri script o procedure.
  2. La funzione, se non altro, pone direttamente nel codice un elenco pressoché completo delle variabili globali utilizzate in questa procedura, senza doverle estrapolare dal file personale che le include assieme a tutte le altre.

Codice.


Void Function ResettaCampi ()
; annulla il contenuto delle variabili globali per:
Let gnAggiorna = FALSE; indicatore d’uscita dal ciclo interno
Let gnCampo = FALSE; campo di elaborazione
Let gnConferma = FALSE; conferme all’uscita
Let gnConta = FALSE; contatore delle chiavi temporanee
Let gnEcoTastiera = FALSE; valore corrente dell’omonima impostazione
Let gnFerma = FALSE; indicatore del tipo d’interruzione
Let gnLivello = FALSE; ultimo campo trattato
Let gnMaxCampi = FALSE; numero complessivo dei campi
Let gnMaxVoci = FALSE; numero delle voci possibili
Let gnMultiple = FALSE; inserimento su più righe
Let gnNumerico = FALSE; prefisso numerico alle voci di scelta
Let gnNuovoScript = FALSE; indicatore di un nuovo script
Let gnProgrammato = FALSE; identificativo di un avvio programmato
Let gnSalta = FALSE; indicatore di salto dei controlli
Let gnScriveTasto = FALSE; trascrizione dei tasti premuti
Let gnStato = FALSE; stato di attivazione della sintesi vocale
Let gnTasto = FALSE; numero del tasto opzionale
Let gnTipo = FALSE; tipologia di procedura
Let gnTotali = FALSE; voci complessive registrate
Let gnValore = FALSE; valore temporaneo
Let gnVoce = FALSE; valore della voce attiva
Let gsAmbito = NULLO; ultimo ambito registrato
Let gsArchivio = NULLO; nome del file di configurazione
Let gsAttiva = NULLO; chiave della voce attiva
Let gsAvviso = NULLO; informazioni dalla procedura
Let gsCategoria = NULLO; tipologia di procedura
Let gsChiama = NULLO; prefisso della funzione chiamata a fine procedura
Let gsChiave = NULLO; chiave attiva del record
Let gsElenco = NULLO; chiavi valide del record
Let gsEtichette = NULLO; elenco dei pulsanti validi
Let gsInfo = NULLO; testo per i messaggi della procedura
Let gsMassimi = NULLO; elenco dei valori massimi per una voce
Let gsMinimi = NULLO; elenco dei possibili valori minimi
Let gsNuoviTasti = NULLO; chiavi dei record con Tasti di attivazione modificati
Let gsOggetto = NULLO; soggetto dell’azione da compiere
Let gsSezione = NULLO; sezione dell’archivio in cui operare
Let gsSuffisso = NULLO; estensione o nome file del documento corrente
Let gsTastiScript = NULLO; Tasti di attivazione dello script
Let gsTermine = NULLO; termine da formattare nei messaggi
Let gsTitolo = NULLO; titolo per la finestra di dialogo
Let gsVoce = NULLO; contenuto della voce attiva
EndFunction

Collaudo.

  1. Anche in questo capitolo, sino alla fine non si potrà che compilare dopo ogni elemento realizzato, e casomai rendere opzionali dei parametri quando ciò venga richiesto.

Esercizio 13.2.2. La funzione PronunciaUscita .

FileScript.

Default.JSS

Nome.

PronunciaUscita

Descrizione.

Concentra in un’unica funzione una serie di comandi per la chiusura dell’elaborazione, compresa la lettura della riga corrente.

Ritorni.

Di tipo Void. Nessuno.

Novità.
  1. La nostra funzione
    ResettaCampi ().
Note.
  1. Questa è una delle prime funzioni chiamate dalla procedura principale, e contiene solo una serie di istruzioni che sono necessarie nei vari punti dai quali si può uscire dalla procedura stessa, tra le quali anche la chiamata alla citata funzione
    ResettaCampi ().
  2. L’unica breve struttura di controllo presente si occupa di attivare la funzione solo quando fosse spenta, poiché la riattiverebbe con specificato il valore per la cancellazione del buffer. Per questo, il controllo deve intervenire solo quando serve, poiché in tutte le altre occasioni rischierebbe di impedire la pronuncia di messaggi precedenti alla chiamata della stessa funzione.

Codice.


Void Function PronunciaUscita ()
suona (ULTIMO_SEGNO); emette un arpeggio discendente
If IsSpeechOff () Then; se la sintesi è spenta,
SpeechOn (TRUE); la riattiva
EndIf; fine controllo stato sintesi
ResettaCampi (); ripulisce la memoria dalle impostazioni,
Refresh (); resetta anche la schermata,
SayLine (); e pronuncia il contenuto della riga corrente
EndFunction

Esercizio 13.2.3. La funzione RilevaCampi.

FileScript.

Default.JSS

Nome.

RilevaCampi

Descrizione.

Cerca nell’archivio di configurazione le etichette di sezione, valide per la configurazione dei campi nelle procedure guidate.

Ritorni.

Di tipo Int. TRUE, per etichette di campi trovate; FALSE, per nessuna etichetta valida.

Fasi.
  1. Il codice è compreso tutto all’interno di un ciclo
    "While"
    , che si ripete finché non viene trovata almeno un’etichetta valida.
  2. Entrati nel ciclo, si rilevano dapprima le sezioni presenti nel file configurazione della categoria impostata nella relativa variabile globale; se tali sezioni esistono, tramite un ciclo
    "For"
    le si scorrono, verificandone il nome.
  3. Se il nome di una sezione non è valido, il ciclo viene subito interrotto; se invece è regolare, essa viene aggiunta alla variabile che fa uscire dal ciclo principale.
  4. Se all’uscita dal ciclo di analisi delle sezioni, tali elementi risultano assenti o non validi, viene consentito di creare o aggiornare il file di configurazione; se questa scelta viene confermata, si azzera l’elenco delle sezioni e si ritorna all’inizio del ciclo generale; se invece si annulla la creazione dei file, il flusso viene interrotto.
  5. Se invece tutti i controlli sono regolari, il ciclo generale viene interrotto restituendo l’esito positivo.
Note.
  1. Come accennato nella premessa, la funzione si occupa di eseguire un primo sommario controllo sui contenuti del file di configurazione per la categoria di procedura scelta, in particolare controllandone il numero e la correttezza delle sezioni di dati inserite; in altre parole, mentre la nostra funzione dello scorso capitolo,
    ArchivioDati ()
    ,creava un nuovo file solo se esso non esisteva, qui si dà la possibilità di ricrearne una versione corretta, qualora vi siano stati rilevati degli errori.

Codice.


Int Function RilevaCampi ()
Var
String sSezioni, ; elenco delle sezioni rilevate
Int iSezioni, ; numero delle sezioni
Int i, ; contatore del ciclo
String sSezione, ; singola sezione estratta
String sSuffisso, ; suffisso numerico in forma testuale
String sTesto, ; prima parte del nome di sezione
Int iValore, ; variabile temporanea
Int iMassimo, ; valore massimo di un campo valido rilevato
String sNumeri, ; elenco dei valori abbinati alle etichette dei campi
String sMessaggio; eventuale avviso per errore nei dati dei campi
While !sNumeri; continua finché l’elenco dei campi rimane vuoto
Let sSezioni = SezioniUtente (gsCategoria); memorizza tutte le sezioni presenti
Let iSezioni = StringSegmentCount (sSezioni, PIPE); conta le sezioni rilevate
For i = 1 To iSezioni; scorre le sezioni nell’archivio
Let sSezione = StringSegment (sSezioni, PIPE, i); estrae la singola sezione
If sSezione != ELABORA Then; se i dati non sono per le impostazioni della singola procedura,
; estrae un eventuale suffisso numerico dalla stringa
Let sSuffisso = EstraeNumero (sTesto, sSezione)
Let iValore = StringToInt (sSuffisso); prova a convertire in numero il suffisso testuale
If sTesto != CAMPO ; se la prima parte della sezione e del prefisso standard sono diverse,
|| !iValore Then; oppure, il suffisso numerico non è un numero valido,
Let i = iSezioni + 1; cambia il contatore, per l’uscita immediata dal ciclo
Else; altrimenti, se il controllo è regolare,
Let sNumeri = CambiaCampo (sNumeri, sSuffisso, ULTIMA); ne aggiunge il dato in coda
If iValore > iMassimo Then; se il suffisso ha un valore maggiore tra quelli rilevati,
Let iMassimo = iValore; aggiorna la variabile
EndIf; fine controllo valore massimo
EndIf; fine controllo ripetizioni
EndIf; fine controllo sezione
EndFor; fine scansione sezioni
Let gnMaxCampi = StringSegmentCount (sNumeri, PIPE); conta i campi validi rilevati
If !gnMaxCampi ; se nessuna sezione è stata trovata,
|| iMassimo != gnMaxCampi Then; oppure, se il numero massimo rilevato non è il totale dei campi,
If !gnMaxCampi && !sSezioni Then; se, oltre ai campi, manca pure il file configurazione,
Let sMessaggio = msgNoFileCampi; imposta la base per segnalarne l’assenza
Else; altrimenti, se non vi sono campi validi, o non in numero sufficiente,
Let sMessaggio = msgNoCampiValidi; imposta la relativa base
EndIf; fine controllo tipo errore
; se si conferma di avviarne la creazione,
If ChiedeConferma (FormatString (sMessaggio, gsCategoria)) Then
; cancella l’eventuale versione esistente del file,
DeleteFile (FormatString (msg2Controbarra, GetUserSettingsDirectory (), gsCategoria + JCF))
Let sNumeri = NULLO; e resetta il dato per riavviare il ciclo di inserimento
If !ConfigurazioneCampi () Then; se i campi non sono stati impostati,
Return FALSE; restituisce un risultato nullo
EndIf; fine controllo impostazione campi
Else; altrimenti, se non si conferma la configurazione dei campi,
Return FALSE; restituisce un risultato nullo
EndIf; fine controllo conferma
Else; altrimenti, se tutti i controlli sono regolari,
Return TRUE; restituisce l’esito positivo
EndIf; fine controllo esito
EndWhile; fine del ciclo principale
EndFunction

Esercizio 13.2.4. La funzione NumeroDati.

FileScript.

Default.JSS

Nome.

NumeroDati

Descrizione.

Controlla se il numero dei dati presenti nel record, relativo ai parametri specificati, equivale a quello registrato nella variabile globale. In ogni caso, trasmette per riferimento il record di dati rilevato.

Ritorni.

Di tipo Int. True, per un numero corretto di dati; FALSE, per un numero errato.

Parametri.
  1. sChiave. La chiave del record da leggere. Di tipo String.
  2. sRecord. Per Riferimento. I dati presenti nel record, trasmessi a prescindere dal numero dei campi rilevati. Di tipo String. Parametro Opzionale.
Note.
  1. Il controllo svolto da tale funzione è molto importante, poiché nell’archivio non possono esserci record con dati incompleti; come analizzeremo più avanti, nel caso di esito negativo di questo controllo, sarà data la possibilità di cancellarli, oppure di completarne il numero.

Codice.


Int Function NumeroDati (string sChiave, string ByRef sRecord)
Let sRecord = LeggeRecord (gsArchivio, gsSezione, sChiave); rileva il record di dati
; se il numero dei campi del record coincide con quelli rilevati nell’archivio,
If StringSegmentCount (sRecord, PIPE) == gnMaxCampi Then
Return TRUE; restituisce l’esito positivo
Else; altrimenti,
Return FALSE; restituisce un risultato nullo
EndIf; fine controllo numero dati
EndFunction

Collaudo.

  1. Dopo aver compilato, il secondo parametro, quello trasmesso per riferimento, deve essere reso opzionale seguendo la solita procedura, in quanto non sarà sempre indispensabile all’elemento di codice chiamante.
  2. Se volete, potete ottimizzare i tempi della modifica richiesta attendendo le successive due funzioni per realizzarla, in quanto anch’esse saranno dotate di parametri opzionali.

Esercizio 13.2.5. La funzione CampoTrovato.

FileScript.

Default.JSS

Nome.

CampoTrovato

Descrizione.

Cerca, e casomai trasmette per riferimento, la posizione del campo indicato all’interno del record di dati specificato.

Ritorni.

Di tipo Int. TRUE, per un campo trovato; FALSE, per nessun ritrovamento.

Parametri.
  1. sElenco. Il record con gli elementi tra i quali cercare. Di tipo String.
  2. sTesto. Il contenuto del campo da cercare. Di tipo String.
  3. iPosiz. Per Riferimento. Il numero del campo individuato. Di tipo Int. Parametro Opzionale.
  4. sSeparatore. Per Riferimento. L’eventuale carattere di separazione da impostare; nel caso in cui non si indichi nulla, sarà usato, e ritrasmesso, il carattere Pipe, Ascii 124. Di tipo String. Parametro Opzionale.
Note.
  1. Questa funzione è utile nelle occasioni in cui è necessario controllare se un dato sia presente in un elenco, o tra i campi di un record; il vantaggio, rispetto al solo utilizzo della funzione integrata
    StringSegmentIndex ()
    , che svolge proprio tale compito, è che la nostra può anche restituire nel contempo la posizione del dato richiesto nell’ambito in cui cercare.
  2. La funzione, inoltre, imposta, e ritrasmette per riferimento, un carattere Pipe come separatore, qualora non ne sia già stato indicato uno come quarto parametro.

Codice.


Int Function CampoTrovato (string sRecord, string sCampo, ; primi due parametri,
int ByRef iPosiz, string ByRef sSeparatore); seconda riga con gli altri due
ImpostaPipe (sSeparatore); se non lo si specifica, imposta come separatore il carattere Pipe
; cerca il contenuto del campo richiesto
Let iPosiz = StringSegmentIndex (StringLower (sRecord), sSeparatore, StringLower (sCampo), TRUE)
If iPosiz Then; se un campo è stato trovato,
Return TRUE; restituisce l’esito positivo
Else; altrimenti,
Return FALSE; restituisce un risultato nullo
EndIf; fine controllo esito ricerca
EndFunction

Collaudo.

  1. Dopo aver completato correttamente la compilazione, anche qui ci sono dei parametri da rendere opzionali, gli ultimi due; ricordiamo, tuttavia, che potete attendere di realizzare anche la prossima funzione , per completare il trittico di nostri elementi per i quali si deve aggiornare il file di documentazione predefinito.

Esercizio 13.2.6. La funzione AggiornaTasti.

FileScript.

Default.JSS

Nome.

AggiornaTasti

Descrizione.

Scrive, aggiorna o cancella le assegnazioni dei tasti abbinate agli script a gestione personale, nel file tasti dell’applicazione corrente, sulla base del tipo specificato come terzo parametro.

Ritorni.

Di tipo Int. Il numero delle modifiche effettuate.

Parametri.
  1. iTipo. L’eventuale tipo di azione da compiere; i valori possibili sono: 0, o FALSE, per aggiornare i dati modificati durante la sessione corrente; 1, o la costante CANCELLA, per rimuovere i dati relativi alla chiave specificata come terzo parametro o registrata nella variabile globale gsChiave; 2, o la costante TUTTI, per controllare la corretta composizione delle assegnazioni relative a tutti gli script a gestione personale per l’applicazione corrente, aggiornando gli script registrati ed eliminando quelli senza riferimenti validi. Di tipo Int. Parametro Opzionale.
  2. sChiave. L’eventuale chiave relativa allo script da rimuovere; qualora non sia indicata, in caso di cancellazione sarà usato il valore impostato nella variabile globale gsChiave. Di tipo String. Parametro Opzionale.
Novità.
  1. Le nostre funzioni
    NumeroDati ()
    e
    CampoTrovato
    , ().
  2. Le costanti
    CANCELLA
    e
    TUTTI
    , che come detto hanno valore, nell’ordine, 1 e 2, e servono per indicare il tipo di azione da compiere sui tasti di attivazione degli script.
Fasi.
  1. Una prima struttura di controllo compone l’elenco delle chiavi dei record da elaborare, sulla base del tipo indicato come primo parametro; qualora si sia specificato di cancellare un’assegnazione tasti, l’eventuale secondo parametro viene usato come chiave del record da eliminare.
  2. Una seconda breve struttura controlla che i dati non siano insufficienti per proseguire, e casomai interrompe il flusso.
  3. Un’ulteriore struttura si occupa di individuare sia le combinazioni tasti per attivare gli script, sia i relativi nomi reali, partendo dalle chiavi rilevate, utilizzando la nostra funzione
    NumeroDati ().
  4. Un ciclo generale scorre poi l’elenco predefinito delle sezioni nei file tasti; all’interno di ciascuna, sulla base dell’azione di scrittura o di rimozione delle assegnazioni, viene comunque controllato, prima della modifica, che a ciascuna assegnazione corrisponda l’esatto nome di script reale; se viene trovata un’assegnazione che coinvolge un nome di script personale che non esiste, essa viene cancellata per rendere disponibili tali tasti per le associazioni con altri script.
  5. Solo nel caso che il controllo sia esteso a tutti gli script, è eseguito dapprima un secondo ciclo per verificare la presenza di precedenti assegnazioni relative ai tasti da aggiornare, servendosi anche della nostra funzione
    CampoTrovato ()
    ; qualora tale ricognizione non completi l’elenco degli aggiornamenti da effettuare, un ulteriore ciclo secondario trascrive le rimanenti coppie di tasti e script a gestione personale.
  6. Al termine delle elaborazioni, se il numero delle modifiche coincide con quelle da effettuare, viene espresso l’esito positivo restituendo il numero delle modifiche stesse; se invece non tutte, o nessuna, delle modifiche sono riuscite, viene restituito un risultato nullo.
Note.
  1. Così come si accennava nella premessa, e come si può intuire dal numero delle fasi, questa funzione è uno dei pezzi principali della procedura; essa consente infatti di gestire ogni esigenza legata alla gestione dei file tasti, dall’aggiornamento, al controllo, ed alla verifica di eventuali impostazioni errate.
  2. La grande attenzione prestata ai controlli, in particolare, è dovuta in questo caso al fatto che l’esatta correlazione, tra gli script a gestione personale e le combinazioni tasti che li attivano, è gestita totalmente da noi; per altri aspetti degli script di tale tipo, si possono invece sfruttare a nostro supporto, come analizzeremo più avanti, anche delle interessanti funzioni integrate di Jaws.

Codice.


Int Function AggiornaTasti (int iTipo, string sChiave)
Var
String sElenco, ; elenco delle chiavi rilevate
Int i, ; contatore di un primo ciclo
String sSezTasti, ; sezione del file tasti
Int j, ; contatore del ciclo generale per le sezioni
Int k, ; contatore del ciclo secondario per le chiavi
Int l, ; contatore del ciclo secondario sugli script
String sRecord, ; Dati dello script
String sTasti, ; tasti di attivazione degli script personali
String sNomi, ; nomi reali degli script
String sChiavi, ; elenco delle combinazioni tasti
String sSezioni, ; elenco delle sezioni del file tasti da elaborare
String sTasto, ; singola combinazione di tasti
String sNome, ; nome dello script
String sDato, ; eventuale nome precedente
Int iPosiz, ; posizione del campo nel record
Int iCambiare, ; numero degli script da aggiornare
Int iModificati, ; script effettivamente cambiati
Int iScript, ; numero degli script personali rilevati
String sElaborati; script personali già trattati
If iTipo == TUTTI Then; se si devono controllare le combinazioni tasti di tutti gli script,
Let sElenco = LeggeChiavi (CORRENTE, gsSezione); rileva le chiavi dall’archivio
ElIf iTipo == CANCELLA Then; se invece ci sono da rimuovere i tasti dello script corrente,
If sChiave Then; se si è specificata una chiave come parametro,
Let sElenco = sChiave; la imposta
Else; altrimenti,
Let sElenco = gsChiave; imposta la chiave attiva
EndIf; fine controllo chiave
Else; altrimenti, se si devono aggiornare gli script modificati durante la sessione,
Let sElenco = gsNuoviTasti; imposta il contenuto dell’apposita variante globale
EndIf; fine controllo elenco chiavi
If !sElenco ; se nessuna chiave è stata rilevata,
&& iTipo != TUTTI Then; e non si è prevista un’analisi globale del file tasti,
Return; interrompe il flusso
EndIf; fine controllo chiavi
If iTipo == CANCELLA Then; se si deve soltanto rimuovere una voce,
Let sTasti = gsRimuoviTasti;imposta la combinazione salvata
; e compone anche il relativo nome reale di script
Let sNomi = FormatString (msgPersonalScript, sElenco)
Else; altrimenti, negli altri casi,
For i = 1 To StringSegmentCount (sElenco, PIPE); scorre le chiavi relative agli script personali
Let sChiave = StringSegment (sElenco, PIPE, i); estrae la singola chiave
If NumeroDati (sChiave, sRecord) Then; se i dati sono presenti nel numero corretto,
; aggiorna gli elenchi, aggiungendo la nuova voce in coda, relativi a:
Let sTasti = CambiaCampo (sTasti, StringSegment (sRecord, PIPE, SECONDA), ULTIMA); tasti
Let sNomi = CambiaCampo (sNomi, FormatString (msgPersonalScript, sChiave), ULTIMA); nomi reali
EndIf; fine controllo numero dati
EndFor; fine scansione chiavi
EndIf; fine controllo tipo aggiornamento
Let iScript = StringSegmentCount (sTasti, PIPE); conta gli eventuali script personali rilevati
Let sSezioni = ElencoSezioni (); imposta le sezioni del file tasti da elaborare,
For j = 1 To StringSegmentCount (sSezioni, PIPE); e le scorre
Let sSezTasti = StringSegment (sSezioni, PIPE, j); estrae la singola sezione da elaborare
If iTipo == CANCELLA Then; se si deve solo cancellare una combinazione tasti,
Let sDato = LeggeDato (APP, sSezTasti, sTasti); rileva il dato dal file tasti corrente
If sDato ; se un nome script abbinato è stato rilevato,
&& sDato == sNomi Then; e coincide con il nome da cancellare,
If CancellaChiave (APP, sSezTasti, sTasti) Then; se la rimozione riesce,
Return TRUE; restituisce l’esito positivo
Else; altrimenti,
Return FALSE; restituisce un risultato nullo
EndIf; fine controllo cancellazione
EndIf; fine controllo dato
Else; altrimenti, se si devono comunque controllare tutte le combinazioni tasti,
Let sChiavi = LeggeChiavi (APP, sSezTasti); rileva l’elenco delle combinazioni tasti
Let sElaborati = NULLO; resetta il dato
For k = 1 To StringSegmentCount (sChiavi, PIPE); scorre le combinazioni di tasti
Let sTasto = StringSegment (sChiavi, PIPE, k); estrae la singola chiave delle assegnazioni
Let sDato = LeggeDato (APP, sSezTasti, sTasto); rileva il nome dello script abbinato
; se la combinazione è tra quelle degli script personali,
If CampoTrovato (sTasti, sTasto, iPosiz) Then
Let sElaborati = CambiaCampo (sElaborati, IntToString (iPosiz), ULTIMA); la aggiunge ad un elenco
Let sNome = StringSegment (sNomi, PIPE, iPosiz); estrae anche il nome reale dello script personale
If sNome != sDato Then; se il nome abbinato è diverso da quello da scrivere,
Let iCambiare = iCambiare + 1; aggiorna il contatore dei dati da aggiornare
If ScriveDato (APP, sSezTasti, sTasto, sNome) Then; se la nuova combinazione è salvata,
Let iModificati = iModificati + 1; aggiorna il contatore dei dati cambiati
EndIf; fine controllo scrittura
EndIf; fine controllo nome
ElIf iTipo == TUTTI Then; se invece l’analisi è estesa a tutti gli script,
If ScriptPersonale (sDato) Then; se si tratta di uno script personale,quindi non valido,
If sTasto Then; se un tasto di cui eliminare l’assegnazione è impostato,
CancellaChiave (APP, sSezTasti, sTasto); rimuove l’assegnazione
EndIf; fine controllo presenza chiave
EndIf; fine controllo validità nome
EndIf; fine controllo combinazioni tasti
EndFor; fine scansione chiavi
If StringSegmentCount (sElaborati, PIPE) < iScript Then; se non tutti gli script sono già presenti,
For l = 1 To iScript; scorre i dati degli script personali
If !CampoTrovato (sElaborati, IntToString (l)) Then; se lo script non è già stato trattato,
Let iCambiare = iCambiare + 1; aggiorna il contatore dei dati da aggiornare
Let sTasto = StringSegment (sTasti, PIPE, l); estrae la chiave con i tasti
Let sNome = StringSegment (sNomi, PIPE, l); estrae anche il nome reale dello script
If ScriveDato (APP, sSezTasti, sTasto, sNome) Then; se la nuova combinazione è salvata,
Let iModificati = iModificati + 1; aggiorna il contatore dei dati cambiati
EndIf; fine controllo scrittura
EndIf; fine controllo presenza elaborati
EndFor; fine scansione script
EndIf; fine controllo numero elaborati
EndIf; fine controllo tipo azione
EndFor; fine scansione sezioni
If iCambiare == iModificati Then; se il numero delle modifiche coincide con quelle da effettuare,
Return iModificati; restituisce il numero dei tasti di attivazione aggiornati
Else; altrimenti,
Return FALSE; restituisce un risultato nullo
EndIf; fine controllo esito aggiornamento
EndFunction

Collaudo.

  1. Se avete atteso fino ad ora per rendere opzionali i parametri di questa e delle due funzioni precedenti, allora, dopo aver correttamente compilato, aprite il file di documentazione predefinito,
    "Default.JSD"
    , cercate la scheda relativa alla prima funzione del gruppo,
    NumeroDati
    , per rendere opzionale il secondo dei due parametri presenti tra quelli elencati.
  2. Sempre proseguendo nelle modifiche cumulative, passate alla scheda,
    CampoTrovato
    , dove la parola chiave è da inserire tra il secondo ed il terzo parametro.
  3. Infine, ed in ogni caso, arrivando alla scheda della presente funzione, i due parametri sono entrambi da porre come opzionali.
  4. Soltanto ora, quindi, si possono salvare le modifiche, compilare nuovamente ed uscire dall’Editor di Script, per rendere effettivi i cambiamenti.

Esercizio 13.2.7. La funzione InizializzaProcedura.

FileScript.

Default.JSS

Nome.

InizializzaProcedura

Descrizione.

Esegue i settaggi iniziali, salvando anche i parametri della procedura nelle apposite variabili globali.

Ritorni.

Di tipo Int. True, per settaggi regolari; FALSE, per dati incompleti.

Parametri.
  1. sSuffisso. L’estensione, o il nome file, del documento corrente. Di tipo String.
Novità.
  1. Le nostre funzioni
    RilevaCampi ()
    e
    AggiornaTasti ().
Fasi.
  1. L’intera funzione è un’unica struttura di controllo, dove la prima via controlla i passaggi nei quali non si devono effettuare le impostazioni iniziali; all’interno di questa opzione, nel primo caso si incrementa il numero del campo trattato, nell’altro ci si limita ad annullare il valore che rende possibile il salto delle citate impostazioni.
  2. La seconda via è quella che viene invece intrapresa nei normali passaggi, quando vi sono da effettuare tutte le impostazioni ed i controlli preliminari; qui si inizia con un breve controllo, effettuato tramite la nostra funzione
    RilevaCampi ()
    , sulla validità dei dati presenti per la procedura, interrompendo il flusso nel caso di esito negativo.
  3. Sempre per la seconda via, qualora il flusso continui, viene impostato il nome globale della sezione dell’archivio dati, sulla base della categoria attiva; durante questa fase viene utilizzata la nostra funzione
    AggiornaTasti ()
    , per controllare l’impostazione preliminare dei file script a gestione personale.
  4. In ogni caso, come scelta predefinita, si inizia impostando il valore che fa iniziare dalla schermata dove si elabora il primo campo di ogni record.
Note.
  1. Questa funzione è soltanto la prima parte della funzione base della procedura, che per semplicità è stata ridotta di dimensioni e, appunto, spezzettata nel maggior numero di funzioni possibili per mantenerne intatta l’efficacia.
  2. Come al solito, ciò è reso possibile dal massiccio ricorso alle variabili globali, che anche in questo caso sono utilizzate sia per dati da elaborare al suo interno, sia per impostazioni da trasmettere agli altri elementi della procedura.

Codice.


Int Function InizializzaProcedura (string sSuffisso)
If gnSalta Then; se si è dopo la procedura di attesa tasti,
If gnFerma Then; se dei tasti sono stati specificati,
Let gnCampo = gnCampo + 1; consente di passare alla schermata successiva
Let gnFerma = FALSE; annulla il valore
Else; altrimenti,
Let gnSalta = FALSE; annulla il valore per il salto della schermata di scelta
EndIf; fine controllo aggiornamenti
Else; altrimenti, in tutti gli altri casi, inizializza il lavoro
If !RilevaCampi () Then; se il rilevamento dei campi ha generato un errore,
Return FALSE; restituisce un risultato nullo
EndIf; fine controllo campi validi
Let gsSuffisso = sSuffisso; rende globale anche il suffisso specificato
Let gnCampo = PRIMA; imposta il valore per la schermata iniziale
If gsCategoria == SCRIPTS Then; se si stanno elaborando gli script a gestione personale,
Let gsSezione = gsCategoria + gsOggetto; compone la sezione nell’archivio dati
AggiornaTasti (TUTTI); Controlla che le assegnazioni degli script personali siano corrette
Else; altrimenti, in tutti gli altri casi, la compone aggiungendovi l’estensione
Let gsSezione = gsCategoria + gsOggetto + gsSuffisso
EndIf; fine controllo categoria
EndIf; fine controllo impostazioni
Return TRUE; restituisce l’esito positivo
EndFunction

***

Funzioni hook .

Le funzioni
hook
sono un particolare elemento di codice che interviene solo quando è attivato, sospendendo il normale funzionamento dei tasti premuti. Grazie a questi speciali elementi sarà possibile riconfigurare ogni effetto delle nostre pressioni sulla tastiera, dando a ciascuna di esse un altro significato rispetto a quello originale.

Per fare un esempio, la modalità Aiuto tastiera, che si attiva premendo
Insert+1
, è il frutto di una funzione hook. Così, premendo la citata combinazione di tasti, la funzione hook può entrare in azione quando sono premuti dei tasti, ripetendo direttamente il loro nome o, soprattutto, indicando se quegli stessi tasti possono in condizioni normali attivare uno script.

Nel nostro caso, le funzioni hook ci servono per conoscere se sia possibile inserire i tasti di attivazione che noi vorremmo assegnare a degli script a gestione personale. Nel dettaglio, l’inserimento dei tasti avverrà in modo tradizionale, grazie ad una modifica che apporteremo ad una funzione evento che conosciamo già, mentre le funzioni hook servono per sospendere i normali effetti dei tasti e per dirci, appunto, quali tra essi sono già occupati da degli script.

Accendere e spegnere una funzione hook.

In particolare, le funzioni di Jaws che noi andremo a sfruttare per gestire la modalità hook sono le seguenti:

  1. AddHook
    , (AggiungiHook).
  2. RemoveHook
    , (CancellaHook).

Come si capisce dalle traduzioni, la prima serve per attivare la modalità che modifica gli effetti della pressione dei tasti, la seconda per cancellarla, ripristinando il normale funzionamento.

Entrambe le funzioni hanno due parametri, che sono uguali tra loro:

  1. un valore che identifica il tipo di modalità hook da attivare, che nel nostro caso è lo 0, o la costante nativa
    HK_SCRIPT.
  2. Il nome della funzione hook cui reindirizzare la gestione dei tasti, che va espresso da solo, senza le parentesi, così come abbiamo già fatto con la funzione
    ScheduleFunction ().

Quindi, mentre le due citate funzioni servono ad accendere e spegnere la modalità hook, l’elemento di codice di cui inseriamo il nome nelle relative istruzioni è quello a cui Jaws reindirizza il controllo dei tasti ai quali è abbinato un qualche script. A questo elemento, che sarà una funzione, potremo assegnare il nome che vogliamo, ed al suo interno andranno posti dei controlli che possano eseguire delle azioni concrete.

A tale funzione, in modo analogo a quello che succede con le funzioni evento, potremo assegnare uno o due parametri, purché di tipo
"string"
, e con un nome a piacere. Essi, tuttavia, avranno già un loro contenuto predefinito: nel nostro caso, sfrutteremo solo il primo, che corrisponde al nome di un eventuale script legato ai tasti premuti.

A questo punto è davvero necessario un esempio per capirci meglio, in cui proporremo una coppia di elementi che avvieranno la modalità speciale, e poi torneranno alla normalità premendo il tasto
"Home"
. Analizzate lo script, che abbiamo chiamato
"ModoAlternativo"
,e la relativa funzione
"NuovoEffetto"
, la quale, una volta attivata la nuova modalità, sarà chiamata a gestire la pressione dei tasti che attivano gli script.


Script ModoAlternativo ()
SayString ("Premere il tasto Home per tornare come prima"); avvisa sul modo di uscirne,
AddHook (HK_SCRIPT, "NuovoEffetto"); ed avvia la modalità speciale
EndScript
Void Function NuovoEffetto (string sNome)
If sNome == "JAWSHome" Then; se si preme il tasto Home,
SayString ("Ecco tornato tutto come prima!"); legge l’avviso,
RemoveHook (HK_SCRIPT, "NuovoEffetto"); interrompe la modalità speciale,
Return TRUE; e restituisce un valore positivo per confermare l’interruzione
Else; altrimenti, in tutti gli altri casi,
SayString (sNome); legge il nome dello script collegato ai tasti premuti
EndIf; fine controllo nome script
EndFunction

In questo caso, gli esempi proposti hanno il seguente funzionamento:

  1. Lo script
    "ModoAlternativo"
    ha solo la funzione di attivare la nuova modalità di risposta alla pressione dei tasti legati a degli script, tramite la funzione nativa
    "AddHook ()"
    . A quest’ultima, si deve specificare come secondo parametro il nome della funzione da attivare, che nel nostro caso abbiamo chiamato
    "NuovoEffetto".
  2. Attivato tale script, tramite la pressione dei tasti ad esso associati, la pressione di qualsiasi altro tasto, o combinazione di essi, che sia legato ad un qualche script, passa per la nostra funzione
    "NuovoEffetto ()"
    ; qui, se lo script collegato ai tasti non viene riconosciuto, il flusso prende la seconda via della struttura di controllo che la compone, limitandosi a pronunciare il nome in inglese dello script.
  3. Quando invece si preme come consigliato il tasto
    "Home"
    , ad esso è collegato lo script nativo
    "JAWSHome ()"
    , il cui nome viene invece riconosciuto dalla prima via del controllo, che intercetta il flusso e, dopo aver pronunciato un avviso, ripristina la situazione precedente tramite la chiamata della funzione nativa
    RemoveHook ()
    ; da quel momento, la modalità ha termine, e tutto torna come era prima.

Se volete, potete provare anche direttamente l’effetto di questi due elementi di codice, semplicemente copiandoli dentro ad un file script, magari quello del Blocco Note, per non rischiare malfunzionamenti irreversibili, ed assegnando allo script una combinazione tasti a vostra scelta.

Disabilitare il controllo di tutti gli altri tasti.

In questa forma degli elementi proposti, l’effetto della pressione dei tasti non legati ad uno script, come ad esempio le lettere o i numeri, rimarrebbe del tutto invariato. Questo succede perché, una volta attivata una funzione hook, non tutti i tasti premuti sono reindirizzati a tale funzione, che in questo caso abbiamo chiamato
"NuovoEffetto ()"
, ma solo appunto quelli legati ad un qualche script.

Per evitare questo risultato, che potrebbe causare degli inconvenienti, è necessario servirsi di una terza funzione nativa,
TrapKeys
, (INtrappolaTasti), che attiva e disattiva la modalità
Trapping
. In tale modalità, infatti, la pressione di tutti i tasti che non sono abbinati ad uno script viene ignorata, senza dunque essere reindirizzata ad alcun elemento di codice.

Questa funzione nativa agisce come un interruttore, disabilitando gli altri tasti quando si pone nel suo primo parametro un valore
"TRUE"
, e tornando ad analizzare tutti i tasti quando si ponga ad esso un valore
"FALSE"
. La funzione avrebbe anche un secondo parametro, che però è opzionale e quindi possiamo anche non considerarlo.

Per concludere questa ampia parentesi sulle funzioni hook, proponiamo gli elementi di prima, ora aggiornati anche con la funzione
TrapKeys ()
. Se volete, potrete casomai provare anche questa versione del codice, avendo comunque l’avvertenza di non farlo all’interno del file script predefinito.


Script ModoAlternativo ()
SayString ("Premere il tasto Home per tornare come prima"); avvisa sul modo di uscirne,
AddHook (HK_SCRIPT, "NuovoEffetto"); avvia la modalità speciale,
TrapKeys (TRUE); e indica a JAWS di ignorare qualsiasi tasto non collegato a script
EndScript
Void Function NuovoEffetto (string sNome)
If sNome == "JAWSHome" Then; se si preme il tasto Home,
SayString ("Ecco tornato tutto come prima!"); legge l’avviso,
RemoveHook (HK_SCRIPT, "NuovoEffetto"); interrompe la modalità speciale,
TrapKeys(FALSE) ; ritorna a far riconoscere a Jaws tutti i tasti,
Return TRUE; e restituisce un valore positivo per confermare l’interruzione
Else; altrimenti, in tutti gli altri casi,
SayString (sNome); legge il nome dello script collegato ai tasti premuti
EndIf; fine controllo nome script
EndFunction

Se avrete modo di provare questa forma aggiornata, una volta attivata la modalità alternativa, solo la pressione dei tasti abbinati a degli script avrà un effetto reale, restituendo come detto il nome inglese degli stessi script. Se si premeranno i tasti singoli, invece, nulla sarà restituito, appunto perché ogni altra pressione sarà ignorata.

Integrare l’effetto delle funzioni hook.

Nella premessa di questo argomento avevamo già accennato al fatto che, per quanto importanti, le funzioni hook coprono solo una parte del lavoro da svolgere. In questo blocco di elementi di codice, infatti, avremo bisogno di:

  1. Attivare una nuova modalità di risposta ai tasti, individuando le volte in cui sono premuti quelli abbinati agli script, e disabilitando la pressione di tutti gli altri.
  2. Intercettare, e registrare, la pressione di combinazioni tasti per attivare gli script a gestione personale.
  3. Gestire la pressione dei tasti Invio, per la conferma dei tasti da assegnare agli script, Escape, per l’annullamento dell’immissione, ed Insert o Tasto Jaws assieme alla lettera T, per la lettura del titolo della finestra.

Tra questi, solo il primo obiettivo si può raggiungere tramite le funzioni hook, mentre per il secondo ed il terzo dovremo trovare un’altra via.

Per capire come fare, si deve analizzare il fatto che anche le funzioni hook, in qualche modo, devono riconoscere i tasti premuti, prima di decidere se gestirli oppure no. Tale compito è svolto da una funzione evento, incontrata nell’ottavo capitolo, che si chiama
PreProcessKeyPressedEvent ().

Se ricordate, questa funzione si attiva appena un tasto viene premuto, restituendo alcuni parametri per riferimento, tra i quali nel secondo il nome per esteso, ed in inglese, di tale tasto. Il modo di sfruttarla sarà, dunque, inserire del codice nella prima parte della funzione nativa, che intercetti, e trasmetta, i dati necessari agli altri elementi della nostra procedura, tramite alcune variabili globali.

L’unico problema è che il nome dei tasti, così come restituito dalla funzione
PreProcessKeyPressedEvent ()
, non è sempre uguale a quello che Jaws usa per abbinarlo ai nomi di script, all’interno dei file tasti con estensione
"JKM"
. Per questo, di seguito produrremo anche una funzione che controlla, e casomai converte, il nome dei tasti, così da renderli compatibili con il resto della procedura.

Nel codice di questo gruppo, composto da altre otto funzioni ed uno speciale script, saranno inserite anche alcune delle istruzioni che abbiamo presentato negli esempi. Per rendere più comprensibili gli elementi proposti di seguito, tuttavia, abbiamo spezzettato in più parti il codice necessario, prevedendo nel dettaglio una funzione che inizializza il lavoro, una che lo avvia ed una che lo ferma, oltre ovviamente a quella che gestisce le varie pressioni, il cui nome è posto come parametro nelle istruzioni con
AddHook ()
e
RemoveHook ().

L’aspetto importante da chiarire è che questa parte della procedura, la quale consente di specificare i tasti che dovranno attivare gli script a gestione personale, è completamente a sé stante. Questo significa, in altre parole, che per lanciarla bisogna di fatto uscire dalla procedura principale, avviare la modalità hook e le altre funzioni accessorie, quindi, al termine dell’inserimento dei tasti, rientrare nella procedura da cui si era usciti.

Esercizio 13.3.4. La funzione AvviaHook.

FileScript.

Default.JSS

Nome.

AvviaHook

Descrizione.

Attiva il reindirizzamento alla funzione hook, il cui nome è registrato in una variabile globale, e resetta il contenuto dell’archivio temporaneo di cattura stringhe.

Ritorni.

Di tipo Void. Nessuno.

Novità.
  1. La funzione integrata
    SetJcfOption
    , (ImpostaOpzioniDiJaws), la quale modifica le opzioni attive di Jaws sulla base dei valori specificati; ha due parametri obbligatori:
    • Il tipo di opzione di cui cambiare il valore, usando una delle costanti presenti nel file predefinito di Jaws, che hanno il prefisso OPT_, per la sintesi vocale, e OPTBRL_, nel caso della barra Braille.
    • Il valore numerico da impostare.
  2. Le altre funzioni native
    AddHook ()
    e
    TrapKeys ()
    ,già illustrate.
  3. La funzione integrata
    IniRemoveSection
    , (CancellaSezioneFileIni), che elimina tutto il contenuto di una sezione di dati in un archivio in formato INI. Ha tre parametri, dei quali solo i primi due obbligatori:
    • Il nome della sezione da cancellare.
    • Il nome dell’archivio da elaborare.
  4. La costante
    TASTI_HOOK
    , che corrisponde alla nostra omonima funzione, e che va messa quindi in tutte le istruzioni dove tale nome deve essere un parametro.
  5. La costante nativa
    OPT_TYPING_ECHO
    ,la quale ha valore 5, e che corrisponde al tipo di opzione da modificare nelle impostazioni per la sintesi vocale.
  6. L’altra costante nativa
    HK_SCRIPT
    , già presentata.
Note.
  1. Questa funzione si occupa, come illustra il nome, di avviare la modalità hook, facendo sì che Jaws sospenda la normale risposta alla pressione dei tasti; essa perciò contiene soltanto l’elenco delle varie istruzioni che servono a tale scopo.
  2. L’assenza di parametri, così come nelle altre occasioni, è permessa dal costante ricorso alle variabili globali.

Codice.


Void Function AvviaHook ()
Suona (INGRESSO_CAMPO); emette un suono di avvio editazione
SetJcfOption(OPT_TYPING_ECHO, 1); imposta l’echo di digitazione nello stato di Caratteri.
AddHook (HK_SCRIPT, TASTI_HOOK) ; attiva l’hook per la cattura dei tasti
SetLastScriptKey (NULLO); azzera i tasti appena premuti
TrapKeys (TRUE); indica a JAWS di ignorare qualsiasi tasto non collegato a script
; cancella la sezione con i dati nell’archivio temporaneo
IniRemoveSection (TASTI, NomeArchivio (TEMPORANEO))
Let gnConta = FALSE; resetta il valore
Let gnScriveTasto = TRUE; attiva la cattura dei tasti
; formatta e legge il messaggio che invita a premere i tasti di attivazione dello script
SayFormattedMessage (OT_HELP, msgAttivazione, NULLO, gsAttiva)
EndFunction

Esercizio 13.3.5. La funzione FermaHook.

FileScript.

Default.JSS

Nome.

FermaHook

Descrizione.

Interrompe una modalità hook per l’intercettazione dei tasti di attivazione degli script, disattivando sia la funzione hook, sia il trapping dei tasti.

Ritorni.

Di tipo Void. Nessuno.

Novità.
  1. La funzione integrata
    RemoveHook
    , già illustrata.
Note.
  1. Questa funzione, in modo del tutto analogo alla precedente, contiene solo un elenco di istruzioni che servono, in questo caso, ad interrompere la modalità hook e a ripristinare la situazione originaria.

Codice.


Void Function FermaHook ()
Suona (USCITA_CAMPO); emette un suono di uscita dall’editazione
Let gnScriveTasto = FALSE; ferma la cattura dei tasti
RemoveHook (HK_SCRIPT, TASTI_HOOK) ; disattiva l’hook
TrapKeys(FALSE) ; ritorna a far riconoscere a Jaws tutti i tasti
SetJcfOption(OPT_TYPING_ECHO,gnEcoTastiera); ripristina l’eco tastiera originale
EndFunction

Esercizio 13.3.6. La funzione ConverteTasti.

FileScript.

Default.JSS

Nome.

ConverteTasti

Descrizione.

Modifica la forma in cui sono stati registrati i tasti premuti, convertendoli nella versione inglese valida per i file JKM.

Ritorni.

Di tipo String. I tasti di attivazione nella forma valida.

Parametri.
  1. sTasti. La stringa da convertire. Di tipo String.
Novità.
  1. Le costanti
    FRECCIA_DESTRA
    e
    FRECCIA_SINISTRA
    , che equivalgono ai rispettivi nomi inglesi dei tasti,
    "RightArrow"
    e
    "LeftArrow".
Fasi.
  1. Un primo ciclo analizza, una alla volta, le eventuali parti dei tasti da controllare, separate dal Segno Più.
  2. Se la porzione di nome analizzata non è il nome inglese dei tasti
    "FrecciaDestra"
    o
    "FrecciaSinistra"
    , tale stringa viene sottoposta ad un secondo ciclo, il quale verifica la presenza al suo inizio di prefissi errati, impostati nel relativo messaggio, e casomai li elimina.
  3. Al termine del ciclo secondario, la stringa eventualmente modificata viene ricomposta, aggiungendovi di nuovo gli eventuali caratteri di separazione, e restituendone poi la forma definitiva al termine anche del ciclo principale.
Note.
  1. Questa funzione si rende necessaria perché, come detto nella premessa, la forma in cui i nomi dei tasti vengono restituiti dalla funzione
    PreProcessKeyPressedEvent ()
    non è sempre uguale a quella utilizzata da Jaws per abbinare gli stessi nomi dei tasti agli eventuali nomi degli script che devono attivare. In particolare, in alcune occasioni, la forma originale prevede un prefisso,
    "Right"
    , (Destra), oppure
    "Left"
    , (Sinistra), davanti a qualche combinazione di tasti presente a destra e sinistra nella tastiera, per indicarne, appunto, la provenienza precisa; come anticipato nelle fasi, quindi, questi suffissi vanno eliminati, prima di passare il nome dei tasti al resto della procedura.

Codice.


String Function ConverteTasti (string sTasti)
Var
Int j, ; contatore del ciclo principale
Int k, ; contatore del secondo ciclo
String sTasto, ; singolo tasto
String sPrefisso, ; singolo prefisso
String sNuova; la combinazione tasti espressa nella forma valida
; scorre le parti del nome separate dal segno unificatore
For j = 1 To StringSegmentCount (sTasti, SEGNO_PIU)
Let sTasto = StringSegment (sTasti, SEGNO_PIU, j); estrae il singolo tasto
If sTasto != FRECCIA_DESTRA ; se quello estratto non è il tasto indicato,
&& sTasto != FRECCIA_SINISTRA Then; e neppure il tasto freccia speculare,
For k = 1 To 2; scorre i due possibili prefissi errati
Let sPrefisso = StringSegment (lstPrefissiTasti, PIPE, k); estrae il singolo prefisso
If StringStartsWith (sTasto, sPrefisso) Then; se il tasto inizia con il prefisso elaborato,
Let sTasto = StringChopLeft (sTasto, StringLength (sPrefisso)); elimina il prefisso dal tasto
EndIf; fine controllo presenza prefisso
EndFor; fine scansione prefissi
EndIf; fine controllo tasti
Let sNuova = CambiaCampo (sNuova, sTasto, ULTIMA, SEGNO_PIU); frappone un segno Più tra i tasti
EndFor; fine scansione tasti
Return sNuova; restituisce la stringa eventualmente modificata
EndFunction

Esercizio 13.3.7. La funzione OrigineScript.

FileScript.

Default.JSS

Nome.

OrigineScript

Descrizione.

Controlla quale sia l’origine di uno script, restituendo un testo opportunamente formattato da proporre in una finestra di dialogo.

Ritorni.

Di tipo String. Il titolo da proporre a video.

Parametri.
  1. sNome. Il nome dello script rilevato. Di tipo String.
  2. iTipo. Per Riferimento. Il tipo di script rilevato. Di tipo Int.
Novità.
  1. Le costanti
    NATIVO
    e
    GESTIONE
    , equivalenti nell’ordine ai valori 1 e 3, che servono ad indicare a quale tipo di script appartenga quello di cui si analizza il nome.
Fasi.
  1. Dopo alcune impostazioni iniziali, una prima struttura di controllo aggiunge all’elenco dei file tasti da elaborare il nome di quello predefinito, qualora non vi sia già.
  2. Un primo ciclo scorre i file tasti definiti, cercando dentro ad essi un’assegnazione che coinvolga i tasti abbinati allo script da cercare.
  3. Un secondo ciclo scorre poi le sezioni da elaborare nei file tasti, sempre arrestandosi nel caso si sia individuato lo script desiderato.
  4. All’uscita dai cicli, o quando essi abbiano concluso la ricerca, qualora si sia trovato lo script richiesto, una struttura di controllo verifica innanzi tutto se sia uno di quelli a gestione personale, ed in tal caso componendo il suo nome reale, o se sia un altro di quelli creati dall’Utente, o infine uno di quelli nativi; in ciascuno di questi casi, la struttura assegna un valore che sarà trasmesso per riferimento.
  5. All’uscita dall’ultimo controllo, viene infine restituito un testo formattato, nel quale si illustra il tipo di script rilevato, al fine di utilizzarlo come titolo in una finestra di dialogo.
Note.
  1. Conoscere di quale tipo sia lo script, che Jaws ha individuato come abbinato ai tasti immessi, serve soprattutto per capire se confermare o meno i tasti premuti; più avanti capiremo anche un altro motivo dell’importanza di tale dato.
  2. Tra i tipi rilevabili, bisogna fare attenzione a non confondere gli script a gestione personale, identificati dalla costante
    GESTIONE
    , con valore 3, e tutti gli altri realizzati dall’Utente secondo il metodo tradizionale, che viene identificato dalla costante
    PERSONALE
    , dal valore 2.

Codice.


String Function OrigineScript (string sNome, int ByRef iTipo)
Var
String sTasti, ; combinazione tasti che attiva lo script
String sFileOrigine, ; elenco dei file con le assegnazioni dei tasti
Int iFile, ; numero dei file tasti
String sArchivio, ; nome completo del file tasti da elaborare
String sSezioni, ; elenco delle sezioni del file tasti da elaborare
Int iNumSezioni, ; numero delle sezioni da elaborare
Int i, ; contatore del ciclo
Int k, ; numero progressivo della sezione elaborata
String sDato, ; dato temporaneo
String sChiave; suffisso numerico dello script
Let sTasti = GetScriptKeyName (sNome); rileva i tasti di attivazione dello script
Let sFileOrigine = ApplicativoCorrente () ; inizializza l’elenco con il nome del file tasti per l’applicazione attiva
If sFileOrigine != JAWS_FILE Then; se il nome non è quello dei file predefiniti di Jaws,
Let sFileOrigine = CambiaCampo (sFileOrigine, JAWS_FILE, ULTIMA); aggiunge il nome predefinito all’elenco
EndIf; fine controllo nome archivio
Let iFile = StringSegmentCount (sFileOrigine, PIPE); conta gli elementi dell’elenco appena creato
Let sSezioni = ElencoSezioni (); imposta le sezioni del file tasti da elaborare,
Let iNumSezioni = StringSegmentCount (sSezioni, PIPE); e ne conta il numero
Let i = PRIMA; inizializza il contatore dei file di origine dei tasti
While !sDato && i <= iFile ; continua finché non si trovi un dato, o vi siano file tasti da elaborare
; compone il nome del file tasti da leggere
Let sArchivio = StringSegment (sFileOrigine, PIPE, i) + JKM
Let k = PRIMA; inizializza il contatore delle sezioni
While !sDato && k < iNumSezioni; continua finché non si trovi un dato, o vi siano sezioni da elaborare
; tenta di leggere un dato dalla sezione estratta del file tasti nelle impostazioni personali
Let sDato = LeggeUtente (sArchivio, StringSegment (sSezioni, PIPE, k), sTasti)
Let k = k + 1; aggiorna il contatore
EndWhile; fine analisi sezioni
Let i = i +1; aggiorna il contatore
EndWhile; fine analisi file tasti
If sDato Then; se un dato è stato trovato,
If ScriptPersonale (sNome, sChiave) Then; se si tratta di uno script a gestione personale,
Let iTipo = GESTIONE; imposta un valore che identifica il tipo di script
Let sNome = LeggeCampo (NULLO, PERSONALI, sChiave, PRIMA); e rileva il nome virtuale dello script
Else; altrimenti, negli altri script creati dall’Utente,
Let iTipo = PERSONALE; imposta il valore relativo
EndIf; fine controllo tipo personale
Else; altrimenti, se si tratta di uno script originale di Jaws
Let iTipo = NATIVO ; ne imposta il valore
EndIf; fine controllo ricerca
Return FormatString (ttlTastiUtilizzati, StringSegment (lstTipiScript, PIPE, iTipo), sNome)
EndFunction

Esercizio 13.3.8. La funzione TastiHook.

FileScript.

Default.JSS

Nome.

TastiHook

Descrizione.

Gestisce i possibili tipi di risposta alla pressione dei tasti, quando si avvia l’attesa della combinazione tasti che attiva uno script.

Ritorni.

Di tipo Void. Nessuno.

Parametri.
  1. sNomeScript. Il nome dello script rilevato dalla funzione hook, trasmesso in modo predefinito alla funzione chiamante. Di tipo String. Parametro Opzionale.
Novità.
  1. Le nostre funzioni
    FermaHook (),
    AvviaHook (),
    OrigineScript ()
    e
    ConverteTasti ().
  2. La costante
    ERRORE2
    , che contiene il nome di un file audio.
  3. Le altre costanti
    INSERT_T
    e
    JAWSKEY_T
    , che equivalgono entrambe alle omologhe combinazioni di tasti.
Fasi.
  1. Nella principale struttura di controllo, la prima via intercetta la pressione del tasto Invio; al suo interno, un controllo iniziale interrompe il flusso nel caso si sia ad un secondo passaggio dopo una sola pressione; quindi, dopo aver preventivamente interrotto la modalità speciale, tramite la nostra funzione
    FermaHook ()
    , si controlla se non siano stati premuti tasti, ed in questo caso si chiede conferma dell’abbandono, altrimenti si torna all’inserimento, grazie alla nostra
    AvviaHook ()
    ; se, infine, tutto è regolare, si registra la penultima pressione dei tasti, che dovrebbe essere quella valida, e si fa proseguire il flusso.
  2. La seconda via della struttura intercetta la pressione del tasto Escape; di diverso dai controlli della prima via, qui la conferma dell’abbandono viene proposta a prescindere dal fatto di aver immesso dei tasti, e comunque in caso di ripensamento si torna all’immissione.
  3. La terza via della struttura intercetta la pressione del tasto Insert+T, oppure JAWSKey+T, producendo la lettura di un messaggio sulla fase di elaborazione. Dopo tale lettura, viene cancellata dalla memoria la pressione dei citati tasti, quindi si torna direttamente alla fase d’inserimento.
  4. L’ultima via della struttura raccoglie tutti gli altri casi nei quali la funzione viene coinvolta, e cioè la pressione di tasti, o combinazioni di essi, che attivano un qualche script; dopo aver come al solito fermato l’hook, la nostra funzione
    OrigineScript ()
    , individua il tipo di script rilevato e crea il messaggio per la finestra di dialogo; nel caso in cui lo script sia uno tradizionale, nostro o nativo, viene proposta la possibilità di sovrascrivere l’impostazione esistente; nel caso di script a gestione personale, invece, si sarà soltanto avvisati dell’impossibilità di utilizzare quei tasti, con l’invito a modificarli; in quest’ultimo caso, o se i tasti scelti non sono confermati negli altri due, si tornerà ad un nuovo inserimento, mentre, se la modifica viene confermata, saranno registrati i tasti premuti, lasciando proseguire il flusso.
  5. Usciti da quella principale, una seconda struttura di controllo verifica se dei tasti siano stati rilevati; se così non è, la prima via della struttura intercetta il flusso e, se dei tasti non sono mai stati specificati neppure in precedenza, si torna alla schermata iniziale di scelta.
  6. Se invece dei tasti sono validi, dapprima la nostra funzione
    ConverteTasti ()
    li rende compatibili con il formato usato per registrarne le assegnazioni nei file tasti
    "JKM"
    ; quindi, se i tasti sono cambiati rispetto ad eventuali assegnazioni precedenti, si tenta di trascriverli nel file tasti e, se la scrittura riesce, si effettuano le impostazioni relative; in particolare, se si tratta di un nuovo inserimento, si imposta un valore per il passaggio diretto alla schermata successiva e, nel caso in cui si tratti comunque di una modifica, viene aggiunta la chiave attiva all’elenco di quelle a cui sono stati modificati i tasti di attivazione.
  7. Terminati anche questi ulteriori controlli, si rientra in ogni caso alla procedura principale, dopo aver impostato il valore per il salto delle impostazioni iniziali.
Note.
  1. Nella prima struttura che intercetta i tasti premuti, nel caso di Invio ed Escape, il controllo viene effettuato confrontando sia i tasti di attivazione legati allo script rilevato dalla funzione hook, sia quelli che sono stati registrati da noi tramite la funzione
    PreProcessKeyPressedEvent ()
    ; in quest’ultimo caso, è proprio la funzione evento che, come analizzeremo più avanti, chiama la funzione hook simulando la sua attivazione; quest’ultima, invece, può avvenire in modo automatico solo nel caso in cui ai citati tasti siano già abbinati degli script.
  2. Il rientro alla funzione principale della procedura, che è in pratica l’ultima istruzione del codice, viene eseguita tramite la chiamata della funzione
    CallFunctionByName ()
    , poiché tale funzione principale dobbiamo ancora realizzarla; con questo stratagemma, infatti, evitiamo qualsiasi problema in fase di compilazione della funzione che stiamo illustrando.

Codice.


Void Function TastiHook (string sNomeScript)
Var
String sTasti, ; ultimi tasti premuti
String sTitolo, ; titolo per la schermata di scelta
Int iTipo, ; tipo di script rilevato
Int iOpzioni, ; scelte possibili nella finestra di dialogo
String sMessaggio, ; testo per il corpo della schermata di scelta
String sPrecedente; eventuale dato preesistente
Let gnFerma = FALSE; annulla l’eventuale valore impostato per attivare la funzione
Let sTasti = LeggeDato (TEMPORANEO, TASTI, IntToString (gnConta)); rileva gli ultimi tasti premuti
If GetScriptKeyName (sNomeScript) == INVIO ; se si è attivato uno script con Invio,
|| sTasti == INVIO Then; oppure, il tasto Invio è stato comunque premuto,
If !sTasti Then; se è presente solo un nome di script,
Return FALSE; restituisce un risultato nullo
EndIf; fine controllo presenza tasti
FermaHook (); disattiva l’hook
If gnConta 

Collaudo.

  1. Dopo aver compilato, ricordiamo di rendere opzionale l’unico parametro della funzione.

Esercizio 13.3.9. La funzione InterrompeAttesa.

FileScript.

Default.JSS

Nome.

InterrompeAttesa

Descrizione.

Controlla l’avvenuta pressione dei tasti che fermano l’attesa dell’immissione di quelli per l’attivazione degli script. Se così è, sono trasmessi dei dati tramite le variabili globali, e viene chiamata la funzione di analisi dei tasti premuti.

Ritorni.

Di tipo Void. Nessuno.

Parametri.
  1. sTasti. Il nome originale dei tasti premuti, comprese eventuali parti identificative da rimuovere. Di tipo String.
Novità.
  1. La prima chiamata della nostra funzione
    TastiHook ()
    , anche se essa avviene non direttamente tramite il codice, bensì grazie alla funzione nativa che consente di porre, come loro primo parametro, i nomi di funzioni da chiamare.
  2. La costante testuale
    TASTI_HOOK
    , che contiene il nome esteso della funzione omologa.
Note.
  1. Il codice è composto da un’unica struttura di controllo, con due livelli annidati l’uno dentro l’altro, dove il flusso entra solo nel caso che l’attesa dei tasti sia stata attivata tramite un valore impostato nell’apposita variabile globale; se così è, i tasti premuti sono registrati in un apposito archivio temporaneo.
  2. Sempre nel caso sia stata attivata l’attesa, se i tasti premuti sono Invio o Escape, sarà chiamata direttamente la funzione che viene coinvolta dalla pressione dei tasti legati agli script, sfruttando l’apposita funzione nativa per indicarla come parametro. Qui l’analisi del codice produrrà o il ritorno alla fase di attesa tasti, oppure l’interruzione di tale attesa, che dovrà essere in ogni caso confermata dall’Utente.

Codice.


Void Function InterrompeAttesa (string sTasti)
If gnScriveTasto Then; se la cattura dei tasti premuti è stata avviata,
Let gnConta = gnConta + 1; aggiorna il contatore delle chiavi,
ScriveDato (TEMPORANEO, TASTI, IntToString (gnConta), sTasti); e vi annota i tasti rilevati
If sTasti== INVIO ; se si è premuto il tasto Invio,
|| sTasti== ESC Then; oppure, se si è premuto il tasto Escape,
CallFunctionByName (TASTI_HOOK); esegue le azioni conseguenti
EndIf; fine controllo tasti
EndIf; fine controllo trascrizione
EndFunction

Esercizio 13.3.10. Lo script MetteMacroTesto5.

FileScript.

Default.JSS

Nome.

MetteMacroTesto5

Sommario.

Inserisce una riga di codice nel documento.

Descrizione.

Chiama l’apposita funzione di scrittura testo, per inserire nel documento il contenuto di una costante che corrisponda alla riga di codice da aggiungere.

TastiAttivazione.

Shift+Control+5

Novità.
  1. La costante
    PRE_PROCESS_KEY
    , la quale contiene l’istruzione che chiama la funzione appena realizzata,
    InterrompeAttesa ().
Note.
  1. Così come gli omologhi script del decimo capitolo, questo serve soltanto a compiere senza errori una modifica ad un elemento di codice nativo, anche in questo caso una funzione evento, il cui contenuto sarà aggiornato seguendo quanto riportato nel prossimo elemento.

Codice.


Script MetteMacroTesto5 ()
MetteTesto (PRE_PROCESS_KEY, TRUE); inserisce la riga di codice, andando poi a capo
EndScript

Collaudo.

  1. Come nei citati precedenti, limitatevi a compilare, perché il test lo faremo nel prossimo elemento, quando eseguiremo la modifica che si deve apportare al codice.

Esercizio 13.3.11. La versione aggiornata di PreProcessKeyPressedEvent ().

FileScript.

Default.JSS

Nome.

PreProcessKeyPressedEvent

Novità.
  1. La nostra funzione
    InterrompeAttesa ().
Note.
  1. La modifica alla versione originale va effettuata individuando la funzione evento all’interno del file script predefinito; non è necessario spostarla in un punto situato dopo le nostre dichiarazioni di inizio codice, in quanto essa chiamerà una nostra funzione senza usare costanti o variabili globali nella sintassi, ma è comunque opportuno farlo anche solo per concentrare il codice da noi manipolato in una sezione apposita del file script predefinito. Se decidete di muovere la funzione, dopo averla spostata, provate subito a compilare una prima volta, per verificare se vi siano problemi con la nuova collocazione.
  2. In ogni caso, posizionatevi all’inizio della prima istruzione all’interno del codice, quindi attivate lo script appena creato premendo
    Shift+Control+5
    , e dovrebbe inserirsi l’istruzione contenuta nella costante
    "PRE_PROCESS_KEY"
    . Al termine di tale modifica, la prima riga della funzione, dopo quella d’intestazione, dovrebbe avere l’aspetto riportato nell’apposita scheda a seguito, dove si è comunque omesso la restante parte del codice originale.
  3. A tal proposito, un’ultima annotazione: il nome della variabile che consente di memorizzare i tasti premuti, la quale nel codice proposto si chiama
    "strKeyName"
    , deve essere necessariamente quello inserito come secondo tra i parametri della funzione; Se nel caso della vostra versione di Jaws tale nome fosse diverso, quest’ultimo dovrebbe essere comunque posto come parametro della funzione
    InterrompeAttesa ().

Codice.


Int Function PreProcessKeyPressedEvent (int nKey, string strKeyName, int nIsBrailleKey, int nIsScriptKey)
InterrompeAttesa (strKeyName); chiama l’apposita funzione per memorizzare i tasti premuti
; spazio per il codice originale
EndFunction

Collaudo.

  1. Se provate a compilare, dovrebbe essere tutto a posto. Non abbiate timore per aver modificato una funzione così importante, perché al momento non cambierà nulla rispetto al solito. Essa inizierà a lavorare per noi, infatti, solo nel caso in cui si attivi una speciale variabile globale. Tale variabile, a sua volta, dovrebbe essere impostata solo da una nostra procedura guidata, che non collauderemo prima del prossimo capitolo.

Esercizio 13.3.12. La funzione CatturaDato.

FileScript.

Default.JSS

Nome.

CatturaDato

Descrizione.

Avvia l’attesa delle combinazioni tasti per gli script.

Ritorni.

Di tipo Void. Nessuno.

Novità.
  1. La funzione integrata
    GetJCFOption
    , (OttieniOpzioniDiJaws), la quale restituisce un valore che rappresenta le opzioni attive di Jaws; tale valore va posto nel suo unico parametro, il quale può essere specificato tramite una delle costanti che abbiamo già analizzato nella funzione
    SetJcfOption ().
Note.
  1. Questa funzione rappresenta quello che, nel nostro esempio di applicazione hook, era costituito dallo script di innesco,
    ModoAlternativo ()
    ; essa contiene la serie di istruzioni che vanno eseguite solo al primo avvio della nuova modalità.
  2. Per quanto appena detto, le istruzioni contenute dentro alla precedente nostra funzione
    AvviaHook ()
    , sono invece quelle che si devono riattivare ad ogni sospensione temporanea della modalità, e per questo la chiamata di tale funzione risulta l’ultima istruzione nel codice sottostante.

Codice.


Void Function CatturaDato ()
Let gsTastiScript = NULLO; resetta il dato da impostare
Let gnEcoTastiera = GetJCFOption(OPT_TYPING_ECHO);salva le impostazioni dell’echo tastiera
Let gnVoce = PRIMA; reimposta la voce corrente sull’unica possibile
Let gnLivello = SECONDA; aggiorna il livello corrente del campo
SpeechOn (TRUE); riattiva comunque la sintesi
AvviaHook (); chiama la funzione per avviare l’hook di cattura tasti
EndFunction

***

Acquisizione e controllo dati .

Dopo aver trattato a parte il blocco riguardante le funzioni hook, rimangono solo altri due elementi che si occupano di completare le impostazioni preliminari, in questo caso di quelle per i singoli campi della procedura. In particolare la seconda funzione sarà quella che viene chiamata direttamente dalla funzione principale, e per questo in essa sono chiamate a confluire le altre funzioni collaterali.

Esercizio 13.4.1. La funzione DatiSchermata.

FileScript.

Default.JSS

Nome.

DatiSchermata

Descrizione.

Rileva e trasmette i dati necessari alle selezioni nella procedura guidata.

Ritorni.

Di tipo Int. TRUE, per i dati recuperati; FALSE, per nessun dato.

Novità.
  1. La nostra funzione
    PronunciaUscita ().
  2. La costante
    MAX_PULSANTI
    , che equivale di norma al valore 5, e che corrisponde appunto al numero massimo dei pulsanti impostabili.
Fasi.
  1. Dopo un tentativo di lettura dati, una prima struttura di controllo verifica che tali dati ci siano, altrimenti si chiama la nostra funzione
    Fasi."PronunciaUscita ()"
    , per eseguire le istruzioni preliminari all’abbandono, prima di interrompere il flusso ed uscire così dalla procedura.
  2. Una seconda struttura definisce il titolo della schermata, sempre sulla base della categoria di procedura attivata.
  3. Dopo aver resettato dei valori iniziali, un ciclo scorre l’archivio cercando i dati dei possibili pulsanti, impostando casomai per ciascuno il limite minimo e massimo necessari per renderlo attivabile.
  4. Nell’ultima parte della funzione, sono letti dall’archivio, ed impostati in variabili globali, una serie di dati relativi alla schermata da elaborare, al termine dei quali viene restituito un valore positivo per far proseguire il flusso.
Note.
  1. Questa funzione, così come spesso accaduto nel capitolo, è un altro pezzo di codice della funzione generale, spostata in un elemento autonomo per semplificarne l’analisi e le eventuali modifiche.

Codice.


Int Function DatiSchermata ()
Var
String sSezione, ;sezione dell’archivio della procedura
Int i, ; contatore del ciclo
String sDati, ; dati rilevati
Int iPrimo, ; indicatore del primo pulsante
String sDato; variabile temporanea
; compone il nome della sezione di cui leggere dall’archivio della procedura
Let sSezione = FormatString (msgCampo, gnCampo)
If !ChiaviUtente (gsCategoria, sSezione) Then; se nessun dato è stato trovato,
; formatta e pronuncia l’avviso,
SayFormattedMessage (OT_ERROR, hlpNoDato, hlpNoDato_corto, FormatString (msgSezioni, sSezione, gsCategoria))
PronunciaUscita (); esegue delle operazioni di chiusura, leggendo la riga corrente,
Return FALSE; e interrompe il flusso, restituendo un risultato nullo
EndIf; fine controllo dati
Let gnTipo = LeggeNumerico (gsCategoria, ELABORA, TIPO); rileva il tipo di procedura impostato
Let sDati = LeggeRecord (gsCategoria, sSezione, TITOLO); rileva l’eventuale titolo memorizzato
If gnTipo == TERZA Then; se si sta operando su più campi,
Let gsTermine = StringSegment (sDati, SPACE, TERZA); estrae la terza parola del titolo
Else; altrimenti, se si è nei primi due tipi di procedura,
; lo prende formattando categoria e oggetto della procedura
Let gsTermine = FormatString (msg2, gsCategoria, gsOggetto)
EndIf; fine controllo termine da formattare
If sDati Then; se un titolo è stato rilevato,
If gnCampo == PRIMA Then; se si è nella schermata iniziale,
Let gsTitolo = FormatString (sDati, gsTermine); personalizza il titolo con l’apposito termine
Else; altrimenti, nei campi dal secondo in poi,
; personalizza il titolo con la voce della schermata iniziale
Let gsTitolo = FormatString (sDati, gsAttiva)
EndIf; fine controllo campi
Else; altrimenti, se il titolo non c’è,
Let gsTitolo = FormatString (ttlSelezione); imposta un titolo generico
EndIf; fine controllo titolo
; resetta i dati per:
Let gsEtichette = NULLO; etichette dei pulsanti
Let gsMinimi = NULLO; valori minimi per attivare un pulsante
Let gsMassimi = NULLO; valori massimi per attivare un pulsante
For i = 1 To MAX_PULSANTI; scorre i possibili dati
; tenta di rilevare le impostazioni
Let sDati = LeggeRecord (gsCategoria, sSezione, FormatString (msgPulsante, i))
If sDati Then; se dei dati sono stati rilevati,
Let sDato = StringSegment (sDati, PIPE, SECONDA); rileva il valore minimo valido per la voce
If !sDato Then; se nessun dato è stato rilevato,
Let sDato = UNO; imposta il valore predefinito in forma testuale
EndIf; fine controllo valore minimo
If !iPrimo Then; se si è al primo passaggio, inizializza gli elenchi per:
Let gsEtichette = StringSegment (sDati, PIPE, PRIMA); etichette dei pulsanti
Let gsMinimi = sDato; valore minimo per attivare il pulsante
Let gsMassimi = StringSegment (sDati, PIPE, TERZA); valore massimo per attivare il pulsante
Let iPrimo = TRUE; valorizza l’indicatore
Else; altrimenti, aggiunge il risultato ai rispettivi elenchi
Let gsEtichette = gsEtichette + FormatString (msgPipe, StringSegment (sDati, PIPE, PRIMA))
Let gsMinimi = gsMinimi + FormatString (msgPipe, sDato)
Let gsMassimi = gsMassimi + FormatString (msgPipe, StringSegment (sDati, PIPE, TERZA))
EndIf; fine controllo passaggi
EndIf; fine controllo dati
EndFor; fine scansione dati pulsanti
; rileva i dati relativi a:
; un prefisso numerico davanti alle voci
Let gnNumerico = LeggeNumerico (gsCategoria, sSezione, NUMERICO)
; le informazioni per i campi di elaborazione testo
Let gsInfo = LeggeRecord (gsCategoria, sSezione, INFO)
; l’assenso all’immissione di voci su più righe
Let gnMultiple = LeggeNumerico (gsCategoria, sSezione, MULTIPLE)
; la conferma all’uscita dalla procedura
Let gnConferma = LeggeNumerico (gsCategoria, sSezione, CONFERMA)
; il prefisso della funzione da chiamare alla fine della procedura
Let gsChiama = LeggeRecord (gsCategoria, sSezione, CHIAMA)
Return TRUE; se i settaggi si sono conclusi, restituisce l’esito positivo
EndFunction

Esercizio 13.4.2. La funzione VociCampo.

FileScript.

Default.JSS

Nome.

VociCampo

Descrizione.

Carica le voci del campo corrente, ed aggiorna le impostazioni della voce attiva.

Ritorni.

Di tipo Int. TRUE, per l’esito regolare del caricamento; FALSE, per interruzioni dovute a errori o procedure da lanciare.

Novità.
  1. Le nostre funzioni
    CatturaDato ()
    e
    DatiSchermata ().
Fasi.
  1. Una prima struttura di controllo verifica se vi siano dei dati validi per la schermata corrente; se tale funzione dà esito negativo, dapprima si controllerà se devono essere inseriti i tasti di attivazione degli script a gestione personale, ed in tal caso si avvia l’apposita procedura uscendo da quella principale, chiamando la nostra funzione
    CatturaDato ()
    ; se invece si era in un’elaborazione a più campi, ma non li si era immessi tutti, allora si tornerà comunque alla prima schermata, aggiornando i valori attivi tramite la nostra
    DatiSchermata ()
    ; se, infine, si è confermata l’uscita dalla procedura, si effettuano le operazioni necessarie.
  2. Se il flusso prosegue, una seconda struttura controlla se ci si trovi lì provenendo da un’altra schermata. se così è, viene emesso un segnale acustico, e sono eseguite delle impostazioni, sulla base del fatto che si sia arrivati o meno alla schermata principale; se invece si è rimasti nella stessa schermata , viene solo attivata la sospensione della sintesi.
  3. In un’ultima e breve struttura, si controlla infine che un qualche valore sia impostato per la voce su cui ci si trova all’ingresso nella schermata; altrimenti, viene comunque impostato il numero 1; all’uscita dalla struttura, in ogni caso, è restituito un valore positivo alla funzione chiamante.

Codice.


Int Function VociCampo ()
If !CercaVoci (gsSezione, gsSuffisso) Then; se il flusso è stato interrotto,
If gnFerma == CATTURA Then; se si devono specificare i tasti per attivare uno script,
CatturaDato (); chiama l’apposita funzione,
Return FALSE; e restituisce un risultato nullo
ElIf gsCategoria == SCRIPTS ; se si stanno elaborando gli script a gestione personale,
&& !gnTotali Then; e se non è ancora stata definita alcuna voce,
If !DuplicaDati () Then; se non è nemmeno stato scelto uno script dall’archivio,
PronunciaUscita (); esegue delle operazioni di chiusura, leggendo la riga corrente,
Return FALSE; e restituisce un risultato nullo
EndIf; fine controllo duplicazione script
ElIf gnTotali && !NumeroDati (gsChiave) Then; se esistono dei campi, ma non tutti,
Let gnCampo = PRIMA; imposta di tornare alla schermata iniziale,
Let gnSalta = FALSE; resetta il valore per l’avvio delle normali schermate,
; DatiSchermata (); ed aggiorna i dati della finestra di dialogo
Else; altrimenti, se si è scelto di uscire dalla procedura,
If gsNuoviTasti Then; se si sono modificate assegnazioni tasti degli script personali,
AggiornaTasti (); trascrive tali modifiche nel file tasti dell’applicazione
EndIf; fine controllo assegnazioni modificate
PronunciaUscita (); esegue delle operazioni di chiusura, leggendo la riga corrente,
Return FALSE; e restituisce un risultato nullo
EndIf; fine controllo indicazione tasti
EndIf; fine controllo voci registrate
If gnCampo != gnLivello Then; se la procedura arriva da un altro campo,
Let gnStato = FALSE; annulla comunque la programmazione della sintesi
If gnCampo == PRIMA Then; se si arriva nella schermata iniziale,
Suona (PRIMO_SEGNO); emette una scala di note ascendenti,
; e ripristina l’ultimo valore confermato del precedente passaggio
Let gnVoce = StringToInt (LeggeRecord (gsArchivio, gsSezione, ZERO))
Else; altrimenti, in tutte le altre schermate,
If !gnSalta Then; se non si proviene da una immissione di testo,
Suona (Nota (gnCampo - 1), RITARDA); emette la nota abbinata al campo corrente
EndIf; fine controllo immissione testo
Let gnVoce = 1; inizializza il valore
EndIf; fine controllo campo
Let gnLivello = gnCampo; risincronizza i valori controllati dal ciclo
Else; altrimenti, se si è rimasti allo stesso livello,
;Let gnStato = TRUE; imposta comunque la programmazione della sintesi
EndIf; fine controllo livelli
If !gnVoce Then; se un valore non è ancora impostato,
Let gnVoce = 1; lo inizializza
EndIf; fine controllo voce corrente
Return TRUE; restituisce l’esito positivo
EndFunction

***

Elenchi a video.

Per analizzare gli elementi di questo blocco, e anche del prossimo, è necessario ora chiarire che la funzione chiave dell’intera procedura è quella nativa di Jaws
DlgSelectItemInList ()
. Essa, se ricordate, propone principalmente una serie di voci poste in un elenco verticale, da scorrere tramite i tasti di movimento.

Quando noi parliamo di elenchi a video, quindi, ci si riferisce proprio a quelli visualizzati nella finestra di dialogo proposta da tale funzione. In questi elenchi, all’interno della nostra procedura, le voci avranno due aspetti principali:

  1. Una stringa testuale, preceduta da un prefisso numerico corrispondente al numero della voce in elenco.
  2. Un nome, di uno script o di un tasto, o altre serie di informazioni, senza alcun riferimento numerico.

Il fatto che sia proposto l’elenco di un tipo o di un altro, dipende dalla categoria di procedura che si sta elaborando. In particolare, la ricerca di elementi e la gestione delle Macro testuali avrà elenchi con prefisso numerico, mentre con gli script personali saranno proposte voci senza alcun prefisso.

Poi, all’interno di quest’ultima modalità di visualizzazione, sulla base del campo elaborato, saranno possibili due ulteriori tipologie:

  1. Gli elenchi con i dati aggiornati dall’Utente.
  2. Un elenco generato con dati predefiniti, non modificabili dalla procedura.

In particolare, la prima funzione di questo blocco è quella che si occupa di generare gli elenchi, determinando se crearli della prima o della seconda tipologia.

A proposito di quest’ultima, produrremo di seguito un apposito titolo, che spiegherà nel dettaglio di che dati si tratti.

Gli ambiti in cui far agire gli script a gestione personale.

Iniziamo col dire che la seconda tipologia di elenco, quella basata su dati predefiniti, viene proposta solo nell’ultimo campo, e quindi dell’ultima schermata, nella procedura di elaborazione degli script a gestione personale.

Inoltre, alla fine dello scorso capitolo, avevamo analizzato che la nostra procedura guidata viene chiamata dalla nostra funzione
GestisceDato ()
, e che quest’ultima agisce solo se si è in una finestra che abbiamo denominato di
"Editing"
. Se ricordate, tra quelle che abbiamo anche già definito, vi sono anche delle altre finestre che, pur essendo comunque di editazione, servono a identificare delle finestre di particolari applicativi, o di finestre secondarie.

Ad esempio, nel caso noi volessimo che un nostro elemento di codice agisca solo nella finestra principale dell’Editor di Script, la finestra corrente dovrebbe corrispondere al tipo che noi abbiamo registrato come
"Manager"
. Oppure, se noi volessimo che funzionasse solo in un ambiente in cui si verifica la posizione effettiva del cursore nel documento, come Microsoft Word o Wordpad, la finestra dovrebbe essere quella da noi identificata come
"Posizione".

Per questo, se noi vogliamo che i nostri script a gestione personale agiscano solo in determinate finestre, dobbiamo avere la possibilità di specificarle. Inoltre, esistono delle ulteriori situazioni, non legate alle finestre, cui vorremmo abbinare certi script, che si comportino in un modo o nell’altro a seconda dei casi.

Per ottemperare a queste diverse esigenze, abbiamo perciò definito con il termine
ambiti
, un dato che noi abbineremo a ciascuno script, condizionando il suo funzionamento al trovarsi in una certa situazione, o quindi appunto, in un certo ambito. Ovviamente, uno script potrà anche essere sempre attivo, senza alcuna condizione, ed allora noi lasceremo l’opzione predefinita come ambiti, cioè
"Nessuno".

Partendo da questa scelta neutra predefinita, a questo elenco saranno poi aggiunte delle azioni che in buona parte avevamo già incontrato nel decimo capitolo,
"Chiude",
"Esce",
"Salva"
e
"Tabella"
, e tutti i tipi di finestre da noi sinora registrati, ad eccezione del tipo
"Editing"
, che è come detto già vagliato a monte dalla funzione di gestione.

Esercizio 13.5.2. La funzione CreaVoci.

FileScript.

Default.JSS

Nome.

CreaVoci

Descrizione.

Crea l’elenco di voci da proporre a video nelle procedure guidate, eventualmente dotandole di prefisso numerico.

Ritorni.

Di tipo String. L’elenco delle voci da visualizzare nelle schermate di scelta.

Fasi.
  1. Una struttura di controllo principale verifica il numero di campo elaborato, indirizzando verso la seconda via, se si tratta dell’ultima schermata della procedura per gli script a gestione personale, oppure alla prima, in tutti gli altri casi.
  2. All’interno della prima via, una ulteriore struttura controlla che alla voce da proporre a video sia posto un prefisso numerico; se così è, le voci da visualizzare sono tutte quelle dell’elenco; se invece un prefisso non c’è, il numero complessivo delle voci, e quella da cui partire, viene stabilito sulla base del tipo di procedura che si sta elaborando.
  3. Sempre dentro alla prima via, viene poi eseguito un ciclo, che crea un elenco di voci sulla base dei dati appena impostati; al suo interno, dapprima si crea il prefisso, qualora esso sia richiesto, poi si rileva tutto il record corrente, casomai estraendone il dato del solo campo che si sta elaborando, per aggiungerlo comunque all’elenco; all’uscita dal ciclo, l’elenco così composto viene restituito.
  4. Nella seconda via, una serie di istruzioni compone l’elenco di scelte prendendo le voci dagli ambiti predefiniti per gli script a gestione personale, e dai tipi di finestra registrati; al termine, viene restituita una versione dell’elenco con il carattere Bell usato come separatore.
Note.
  1. Nell’elenco creato, qualora fosse composto di più voci, ciascuna sarebbe separata dall’altra tramite il carattere Bell, Ascii 7; questo per consentire che, eventuali presenze nel testo delle singole voci di un carattere Pipe, il separatore predefinito degli elenchi, non crei degli errori di visualizzazione dell’elenco stesso.

Codice.


String Function CreaVoci ()
Var
Int iStart, ; valore dal quale far partire il ciclo
Int iValide, ; numero delle voci da inserire nell’elenco
Int i, ; contatore del ciclo
String sChiave, ; singola chiave da elaborare
String sRecord, ; dati registrati
String sDato, ; voce temporanea
String sNum, ; prefisso numerico
String sVoci, ; elenco da restituire
String sTipi; tipi di finestra registrati
If gsCategoria != SCRIPTS ; se non si stanno elaborando gli script a gestione personale,
|| gnCampo < gnMaxCampi Then; oppure, se non si è nell’ultimo campo,
If gnNumerico Then; se si deve porre il prefisso numerico,
Let iStart = PRIMA; imposta il valore di partenza
Let iValide = gnTotali; propone tutte le voci dell’elenco
Else; altrimenti, se l’le voci sono senza numeri,
If gnCampo == PRIMA Then; se se si sta elaborando il primo campo,
Let iStart = PRIMA; imposta di partire dalla voce iniziale,
Let iValide = gnTotali; e fa coincidere il numero totale delle voci con quelle da creare
Else; altrimenti, per i campi dal secondo in poi,
Let iStart = StringSegmentIndex (gsElenco, PIPE, gsChiave, TRUE); rileva la posizione della voce attiva,
Let iValide = iStart; e duplica il valore per limitare le voci ad una
EndIf; fine controllo campo
EndIf; fine controllo prefisso
For i = iStart To iValide; scorre il numero di voci definito
Let sChiave = ChiaveCorrente (i); compone la chiave attiva sulla base del tipo di procedura
If gnNumerico Then; se si è indicato di aggiungere il prefisso numerico alle voci,
Let sNum = FormatString (msgNumerico, sChiave); lo imposta
EndIf; fine controllo prefisso
Let sRecord = LeggeRecord (gsArchivio, gsSezione, sChiave); rileva l’intero record di dati
; estrae il dato del solo campo elaborato, casomai premettendovi il prefisso numerico
Let sDato = sNum + StringSegment (sRecord, PIPE, gnCampo)
; e lo aggiunge all’elenco frapponendovi il carattere Bell, Ascii 7
Let sVoci = CambiaCampo (sVoci, sDato, ULTIMA, BELL)
EndFor; fine scansione chiavi
Return sVoci; restituisce il nuovo elenco con i dati
Else; altrimenti, nell’ultimo campo degli script personali,
; rileva i tipi di finestre registrati, rimuovendo il tipo Editing in quanto non selezionabile
Let sTipi = CambiaCampo (LeggeChiavi (PERSONALE, TIPI_FINESTRE), EDITING)
; compone l’elenco con una voce neutra, gli ambiti predefiniti e i tipi di finestra registrati
Let gsElenco = FormatString (msg3Pipe, NESSUNO, lstAmbitiScript, sTipi)
; rileva l’eventuale dato presente nell’ultimo campo del record
Let gsAmbito = LeggeCampo (gsArchivio, gsSezione, gsChiave, gnMaxCampi)
If !CampoTrovato (gsElenco, gsAmbito, gnVoce) Then; se il dato nell’elenco non c’è,
Let gnVoce = 1; imposta di posizionarsi sulla prima voce dell’elenco
EndIf; fine controllo posizione dato
Return ConverteInBell (gsElenco, PIPE); restituisce l’elenco, sostituendo il separatore
EndIf; fine controllo campo
EndFunction

***

Evitare di ripetere le informazioni nell’aggiornare le schermate.

Abbiamo appena citato la funzione nativa
DlgSelectItemInList ()
, della quale nello scorso capitolo avevamo illustrato anche la gestione dei tasti alternativi. Essa, ricapitolando, ci consente di:

  1. Selezionare una voce dall’elenco, confermandola poi con Invio.
  2. Premere un pulsante opzionale, per indicare al codice di eseguire questa o quella azione specifica.

Il fatto importante da evidenziare è che, in entrambi i casi, con la pressione di un qualsiasi

tasto, il lavoro della funzione si conclude, restituendo il flusso all’istruzione successiva.

In questo modo, la pressione di un tasto nella finestra di dialogo, proposta dalla funzione nativa, potrebbe avere tre sviluppi principali:

  1. Con Invio o Escape, la conclusione della fase di elaborazione, o l’uscita dalla procedura.
  2. Con un tasto opzionale che comporta il passaggio ad un altro campo, la proposizione a video di una nuova schermata della stessa procedura.
  3. Con un tasto opzionale che modifica i dati presenti a video, il solo aggiornamento della schermata corrente.

In tutti e tre i casi,
DlgSelectItemInList ()
potrebbe essere richiamata, anche se, nella prima opzione, solo se non si confermasse l’uscita. Nei casi 2 e 3, invece, il codice farà comunque un ciclo, tornando poi a chiamare la citata funzione nativa, nel secondo caso, mostrando le nuove informazioni legate all’ulteriore schermata, e nel terzo caso, ripetendo il titolo e le informazioni accessorie.

Sul piano pratico ciò significa che, nell’ultimo caso di quelli analizzati, noi avremmo lo stesso titolo della finestra di dialogo, e la stessa posizione della voce nell’elenco, che saranno ripetuti ad ogni chiamata della funzione.

Per evitare di ripetere inutilmente le informazioni ridondanti, sarà perciò necessario sospendere la lettura della sintesi vocale, per un tempo sufficiente alla funzione per riproporre a video tali informazioni in modo silenzioso. La sintesi sarà poi riattivata tramite un avvio programmato, che abbiamo conosciuto nello scorso capitolo, e che in questo caso si occuperà anche di leggere la sola voce posta sul cursore.

Per gestire lo spegnimento e la riaccensione programmata della sintesi, dunque, ci sono le prossime due funzioni, la prima complementare alla seconda. Esse sono gestite come al solito tramite variabili globali, che sono impostate prima della, o durante la, chiamata di tali funzioni.

Per far sì che la voce di Jaws sia subito zittita, nelle relative funzioni sarà specificato anche l’azzeramento del buffer della sintesi vocale, tramite l’uso del valore
"TRUE"
nell’apposito parametro. Per garantire che questo azzeramento non provochi la perdita di qualche informazione, in questo blocco useremo anche la funzione integrata
QueueFunction
, (FunzioneCoda), la quale esegue la funzione, specificata come unico parametro, finché un’eventuale pronuncia di testo non si sia conclusa.

In altre parole, tramite essa ci si accerta che l’istruzione successiva sia eseguita solo dopo che la funzione indicata abbia concluso il suo compito, per evitare di perdere, in tutto o in parte, il contenuto pronunciato in precedenza. al nome della funzione si possono aggiungere eventuali parametri, purché siano numeri, o testi tra virgolette, compresi tra parentesi tonde.

Esercizio 13.6.1. La funzione AttivaSintesi.

FileScript.

Default.JSS

Nome.

AttivaSintesi

Descrizione.

Riattiva la sintesi, pronunciando, o l’eventuale avviso impostato, o la voce attiva, oppure, altrimenti, la riga corrente.

Ritorni.

Di tipo Void. Nessuno.

Novità.
  1. La funzione integrata
    SayUsingVoice
    , (LeggiUsandoVoce), la quale pronuncia un testo tramite una specifica voce di Jaws; ha tre parametri:
    • Una stringa che indica con quale voce pronunciare il messaggio, usando le costanti testuali che, nel file predefinito di Jaws, hanno il prefisso VCTX_.
    • Il testo del messaggio da leggere.
    • Un valore che indica il tipo di output, quelli che nel file predefinito delle costanti hanno il prefisso OT_.
  2. Le costanti native
    VCTX_JAWSCURSOR
    e
    VCTX_PCCURSOR
    , che equivalgono ciascuna al nome posto dopo il prefisso standard, e che indicano di utilizzare per i messaggi, rispettivamente, la voce del cursore Jaws e quella del cursore PC.
Note.
  1. La spiegazione dell’unica struttura di controllo che costituisce la funzione è già illustrata nella descrizione; l’unico aspetto da sottolineare è che le due opzioni principali di utilizzo sono la lettura dell’avviso o della voce attiva; il contenuto della riga, infatti, sarà letto solo nel caso in cui, per un qualche errore della procedura, nessuno dei due precedenti dati fosse impostato.

Codice.


Void Function AttivaSintesi ()
SpeechOn (TRUE); riattiva la sintesi, resettando il buffer
If gsAvviso Then; se un messaggio è stato impostato,
SayUsingVoice (VCTX_JAWSCURSOR, gsAvviso, OT_HELP); lo pronuncia,
Let gsAvviso = NULLO; e lo resetta
ElIf gsVoce Then; se invece è impostata la voce attiva,
SayUsingVoice (VCTX_PCCURSOR, gsVoce, OT_HELP); la pronuncia
Else; altrimenti, se nessun testo è impostato,
SayLine (); pronuncia il contenuto della riga corrente
EndIf; fine controllo testo da leggere
EndFunction

Esercizio 13.6.2. La funzione ControllaProgrammato.

FileScript.

Default.JSS

Nome.

ControllaProgrammato

Descrizione.

Verifica che la sintesi sia stata spenta dopo la schermata di scelta, e quindi che debba essere riattivata solo tramite un avvio programmato.

Ritorni.

Di tipo Void. Nessuno.

Novità.
  1. La nostra funzione
    AttivaSintesi ()
    ,anche se chiamata tramite l’omologa nostra costante
    ATTIVA_SINTESI
    , anch’essa da annoverare quindi tra le novità della funzione.
  2. La funzione integrata
    QueueFunction ()
    , già illustrata nella premessa.
Fasi.
  1. Una prima struttura di controllo verifica se sia stato impostato un avviso; se così è, per pronunciarlo viene chiamata la nostra funzione
    AttivaSintesi ()
    ,immessa come parametro alla funzione integrata
    QueueFunction ().
  2. Una seconda struttura controlla invece se si debba impostare un avvio programmato della sintesi; se così è, prima si annulla un’eventuale programmazione precedente, poi si imposta quella nuova; se invece la sintesi è ancora spenta, la si riattiva in modo immediato.

Codice.


Void Function ControllaProgrammato ()
If gsAvviso Then; se un avviso è stato impostato,
QueueFunction (ATTIVA_SINTESI); lo pronuncia, impostando l’attesa per la fine del parlato
EndIf; fine controllo avviso
If gnStato Then; se la sintesi va riavviata in modo programmato,
SpeechOff (TRUE); spegne in ogni caso la sintesi
AnnullaProgrammato (); cancella un eventuale avvio programmato già impostato
Let gnProgrammato = ScheduleFunction (ATTIVA_SINTESI, RITARDA); e ne imposta uno nuovo
Let gnStato = FALSE; annulla il valore
ElIf IsSpeechOff () Then; se la sintesi è ancora spenta,
SpeechOn (TRUE); la riattiva
EndIf; fine controllo stato sintesi
EndFunction

***

I controlli d’uscita.

In questo breve blocco, i due elementi sono legati al compito di gestire i controlli necessari al’uscita dalla procedura. Non che il nostro lavoro sia finito qui, tutt’altro, ma solo perché già da questo punto, sulla base dei controlli e delle conferme rilasciate dall’Utente, è possibile concluderla.

In particolare, va evidenziata La prima delle due funzioni, quella che si occupa di segnalare, e casomai cancellare, i record inseriti che non avessero un numero di dati sufficienti per essere ritenuti validi. L’altra invece, si occupa solo di gestire l’eventuale uscita, richiamando le stesse funzioni che abbiamo già analizzato nelle procedure d’inizio.

Esercizio 13.7.1. La funzione DatiCompleti.

FileScript.

Default.JSS

Nome.

DatiCompleti

Descrizione.

Restituisce l’esito del controllo se i dati relativi alle chiavi indicate abbiano un numero corretto di campi, altrimenti consente di cancellarli o di tornare alla loro modifica.

Ritorni.

Di tipo Int. True, per dati regolari; FALSE, per dati incompleti, e ritorno alla procedura.

Parametri.
  1. sIgnora. L’eventuale elenco delle chiavi da escludere durante il controllo. Di tipo String. Parametro Opzionale.
Novità.
  1. La costante
    SU
    , che corrisponde al termine omonimo, utilizzato per formattare un messaggio.
Fasi.
  1. Un primo ciclo scorre le chiavi dei record da controllare, ad eccezione di quelli inseriti nell’eventuale elenco specificato come parametro; se i dati del singolo record sono incompleti, la relativa chiave viene aggiunta ad un elenco di errori.
  2. Alla fine del ciclo, se non è stato rilevato alcun errore, si restituisce subito l’esito positivo;
  3. Se vi sono dei record con dati incompleti, si propone la conferma alla loro cancellazione, o al ritorno nella procedura per il necessario aggiornamento.
  4. Se invece si conferma di eliminarli, un secondo ciclo scorre l’elenco delle chiavi con i record errati e li cancella; sia che la rimozione abbia buon esito, oppure che fallisca, viene comunque proposto un messaggio con tale esito, restituendo in ogni caso un valore positivo.

Codice.


Int Function DatiCompleti (string sIgnora)
Var
Int i, ; contatore del ciclo
String sChiave, ; chiave del singolo record
String sRecord, ; record di dati
String sChiavi, ; record non validi
String sDati, ; dati principali dei record errati
String sDato, ; singolo dato del record
String sTitolo, ; titolo della finestra di conferma
Int k; contatore dei record rimossi
For i = 1 To StringSegmentCount (gsElenco, PIPE); scorre le chiavi dei record
Let sChiave = ChiaveCorrente (i); estrae la singola chiave
If !CampoTrovato (sIgnora, sChiave) Then; se la chiave del record non è da escludere,
If !NumeroDati (sChiave, sRecord) Then; se il numero dei dati è errato,
Let sDato = StringSegment (sRecord, PIPE, PRIMA); estrae il dato iniziale
Let sChiavi = CambiaCampo (sChiavi, sChiave, ULTIMA); aggiunge la chiave in coda
Let sDati = CambiaCampo (sDati, sDato, ULTIMA, VIRGOLA_SPAZIO); pone una virgola tra i dati
EndIf; fine controllo dati validi
EndIf; fine controllo chiavi valide
EndFor; fine scansione chiavi
If !sChiavi Then; se tutti i record sono completi,
Return TRUE; restituisce l’esito positivo
EndIf; fine controllo chiavi valide
If sDati Then; se l’elenco non è vuoto,
Let sDati = FormatString (msgVirgola, sDati); aggiunge una virgola alla fine dell’elenco
EndIf; fine controllo dati principali errati
; formatta il titolo della finestra di dialogo con la conferma
Let sTitolo = FormatString (ttlDatiMancanti, StringSegmentCount (sChiavi, PIPE), sDati)
If !ChiedeConferma (sTitolo, msgRimozioneDati,FALSE, SECONDA) Then;se non si vuole continuare,
; aggiorna la posizione del puntatore della voce attiva
Let gnVoce = StringToInt (TroncaTesto (sChiavi))
Return FALSE; e restituisce un risultato nullo
EndIf; fine controllo conferma
For i = 1 To StringSegmentCount (sChiavi, PIPE); scorre i record da rimuovere
; se la cancellazione riesce,
If CancellaChiave (CORRENTE, gsSezione, StringSegment (sChiavi, PIPE, i)) Then
Let k = k + 1; aggiorna il contatore
EndIf; fine controllo cancellazioni
EndFor; fine scansione record errati
Let i = i - 1; toglie un’unità al contatore per renderlo effettivo
If k == i Then; se tutte le cancellazioni sono riuscite, formatta e legge l’avviso
SayFormattedMessage (OT_ERROR, hlpRimuoviRecord, NULLO, k, PersonaMaschile (k))
Else; altrimenti, se ci sono stati degli errori,
If !k Then; se nessuna cancellazione è riuscita, propone il numero delle mancate rimozioni
SayFormattedMessage (OT_ERROR,hlpErroreRecord, NULLO, CANCELLA, i)
Else; altrimenti, se la cancellazione è stata parziale
; mostra entrambi i valori
SayFormattedMessage (OT_ERROR,hlpErroreRecord, NULLO, CANCELLA, i - k, SU, i)
EndIf; fine controllo tipo errore
EndIf; fine controllo cancellazioni
Return TRUE; restituisce l’esito positivo
EndFunction

Collaudo.

  1. Dopo aver correttamente compilato, ricordiamo di porre come opzionale l’unico parametro della funzione.

Esercizio 13.7.2. La funzione UscitaProcedura.

FileScript.

Default.JSS

Nome.

UscitaProcedura

Descrizione.

Gestisce la richiesta di concludere la procedura.

Ritorni.

Di tipo Int. TRUE, per la conferma all’uscita; FALSE, per il ritorno alla procedura.

Novità.
  1. La nostra funzione
    DatiCompleti ().
Fasi.
  1. Una struttura principale di controllo, nella sua prima via, fa ritornare alla schermata iniziale, in tutti i casi in cui si stia elaborando un campo dal secondo in poi.
  2. Se nelle impostazioni del campo elaborato non sono previste conferme all’uscita, o se tale conferma è stata fornita nella relativa finestra di dialogo, la seconda via della struttura esegue in sequenza le impostazioni di abbandono della procedura, utilizzando anche la nostra funzione
    DatiCompleti ()
    ; se quest’ultima restituisce un risultato nullo, si torna alla fase di elaborazione, altrimenti si esce dalla procedura, chiamando la nostra apposita funzione.
Note.
  1. La presente funzione viene chiamata in tutti i casi nei quali, durante l’elaborazione delle schermate, si preme il tasto Escape, o il pulsante
    "Annulla".

Codice.


Int Function UscitaProcedura ()
If gnCampo > PRIMA Then; se si è dalla seconda schermata in poi,
Let gnCampo = PRIMA; imposta di tornare all’inizio,
Let gnAggiorna = TRUE; e di uscire dal ciclo di aggiornamento campo
ElIf !gnConferma ; se invece non è prevista alcuna conferma all’uscita,
|| ChiedeConferma () Then; oppure, se la conferma all’uscita ha avuto esito positivo,
SpeechOn (TRUE); riattiva comunque la sintesi
; controlla se i dati registrati sono validi, dovendo avere ciascuno il giusto numero di campi
If DatiCompleti (ZERO) Then; se così è,
If gsNuoviTasti Then; se si sono modificate assegnazioni tasti degli script personali,
AggiornaTasti (); trascrive tali modifiche nel file tasti dell’applicazione
EndIf; fine controllo assegnazioni modificate
PronunciaUscita (); esegue delle operazioni di chiusura, leggendo la riga corrente,
Return TRUE; e restituisce la conferma all’uscita
EndIf; fine controllo validità record
Else; altrimenti, se i dati non sono completi, e si deve tornare alla procedura,
Let gnStato = ControllaSintesi (FALSE, TRUE); spegne la sintesi, con l’azzeramento del buffer
EndIf; fine controllo conferme
EndFunction

***

Riepilogo.

Con l’esigenza di dividere a metà l’argomento, e la necessità di mantenere lo sviluppo delle informazioni in modo sequenziale, i contenuti di questo capitolo sono stati indubbiamente resi marginali rispetto a quelli che potremo esprimere nel prossimo.

Di rilievo, tuttavia, l’accenno alle funzioni hook, che è uno strumento molto potente, sia pure con limitate applicazioni pratiche.

Portate pazienza, quindi, ma ormai ci siamo: nel prossimo capitolo sono concentrate tutte le novità e gli aspetti più interessanti di tutto l’argomento.

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