Numero 10 del 2021
Titolo: Gli script di Jaws 13e. Le procedure guidate: controllo dei dati e funzioni hook.
Autore: Abramo Volpato.
Articolo:
Gli script di Jaws 13e. Le procedure guidate: controllo dei dati e funzioni hook.
Di Abramo Volpato.
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
13.5. 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.
13.5.1. 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 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
13.6. 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
13.7. 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.
***
Per ulteriori spiegazioni, scrivere a: Abramo Volpato, oppure a: Nunziante Esposito.