Numero 06 del 2021
Titolo: Gli script di Jaws 11. Le serie di Script e il nuovo Aiuto in Linea, prima parte.
Autore: Abramo Volpato.
Articolo:
Gli script di Jaws 11. Le serie di Script e il nuovo Aiuto in Linea, prima parte.
Di Abramo Volpato.
Prima parte.
11.1. Facciamo lavorare Jaws per noi.
Lo scopo del nostro lavoro con gli script di Jaws è, come detto più volte, semplificarci le PERSONALI attività quotidiane al computer. In questo capitolo, ed in quelli che seguiranno, faremo in modo che sia Jaws a lavorare per noi, o meglio, che faccia per noi la parte più ripetitiva del nostro lavoro.
In particolare, ora esamineremo come far sì che Jaws inserisca, da dentro all'Editor di Script, delle serie di copie dello stesso script base, da noi realizzato, modificandoci di volta in volta solo il suffisso numerico. Questi gruppi di script, i cui nomi saranno dunque diversi solo per l'ultima cifra che li contraddistingue, in questo capitolo andranno solo dall'uno al nove, ma più avanti avranno suffissi di numero anche maggiore.
La nascita di script in serie, che useremo pressoché in ogni occasione da qui alla fine del nostro viaggio negli script di Jaws, ci porterà poi a modificare il sistema che avevamo approntato per creare un aiuto in linea. Esso, per non appesantire troppo le informazioni inviate al Visualizzatore Virtuale, dovrà essere in grado di riconoscere se uno script appartiene ad una serie e, in tal caso, dovrà indicarne solo i dati riassuntivi.
Coglieremo poi l'occasione per inserire in questa versione le funzionalità definitive del nostro aiuto in linea, che ci consentirà di avere informazioni di contesto anche in altre procedure da realizzare. Per fare ciò, dovremo per forza anticipare sia alcuni aspetti di carattere generale, sia alcuni dettagli, che riguarderanno il resto del lavoro da svolgere.
Prima, però, , ci dedichiamo al consueto aggiornamento dei nostri file esterni.
Esercizio 11.1.1. Aggiornare il file Personale delle Costanti.
A9 = "\T", ; carattere Tab, Ascii 9
CONTROL_R = "Control+R", ; combinazione tasti omologa
CHIAVE_SCRIPT = "Script ", ; parola per identificare l'intestazione di uno script
APERTA_TONDA = "(", ; carattere omonimo
MAX_VOCI = 99, ; numero predefinito per le voci in una finestra di elaborazione
TASTIERA = 1, ; indica l'input tramite la tastiera del pc
JSS = ".JSS", ; estensione omonima
SEGNO_PIU = "+", ; carattere omonimo
BACKSPACE = "BackSpace", ; tasto omonimo
CONTROL_E = "Control+E", ; combinazione omologa
TAB = "Tab", ; tasto omonimo
BARRA_SPAZIO = "Space", ; l'equivalente inglese del tasto omonimo
ALT_D = "Alt+D", ; combinazione omologa
INVIO = "Enter", ; tasto inglese omonimo
DELETE = "Delete", ; tasto omonimo
CONTROL_S ="Control+S", ; combinazione tasti omologa
AMPERSAND = "&", ; termine inglese per il carattere
ALT = "Alt", ; termine omonimo
ALT_O = "Alt+O", ; combinazione tasti omologa
EDITOR_ITA = "editor di script", ; nome italiano dell'applicazione omonima
EDITOR_ENU = "script editor", ; nome inglese dell'applicazione omonima
PERSONALI = "ScriptsPersonali", ; sezione dei dati sugli script a gestione personale
Esercizio 11.1.2. Aggiornare il file Personale delle Variabili Globali.
string gsTitolo, ; titolo della finestra di dialogo
String gsEtichette, ; elenco di etichette
String gsTermine, ; termine usato per formattare il titolo delle schermate
int gnCampo, ; fase attiva della procedura
String gsArchivio, ; il nome, senza estensione, dell'archivio da elaborare
Esercizio 11.1.3. Aggiornare il file Personale dei Messaggi.
; nessuno script rilevato sul cursore
@hlpNoScript
Nessuno script alla posizione del cursore
@@
; nessuno script rilevato sul cursore - versione corta
@hlpNoScript_corto
Nessuno script.
@@
; quattro termini affiancati
@msg4
%1%2%3%4
@@
; conferma generica
@msgConferma
Confermate?
@@
; Nessun nome di file individuato
@hlpNoNomeFile
Nessun NomeFile Rilevato dal documento corrente.
@@
; Nessun nome di file individuato - versione corta
@hlpNoNomeFile_corto
Nessun NomeFile.
@@
; nessun suffisso numerico nel nome
@hlpNoSuffisso
Nessun suffisso numerico in %1.
@@
; nessun suffisso numerico nel nome - versione corta
@hlpNoSuffisso_corto
Nessun suffisso.
@@
; Titolo per sezione o tasti di attivazione mancanti in uno script
@ttlNoTasti
Sezione o tasti mancanti per lo script %1
@@
; messaggio su sezione o tasti di attivazione mancanti in uno script
@msgNoTasti
Continuare senza aggiungerli?
@@
; scelta ultimo suffisso numerico per la serie di script
@msgUltimoScript
Scegliere il suffisso per l'ultimo script della serie
@@
; titolo per conferma creazione script
@ttlCreaScript
Lo script %1 sarà copiato fino al suffisso %2.
@@
; conferma di scelta generica
@msgConfermaScelta
Confermate la scelta?
@@
; comando per simulare la pressione di un tasto
@lstPremiTasto
TypeKey ("%1")
@@
; titolo delle informazioni sulle finestre di dialogo
@ttlInfoDialogo
Informazioni sulla finestra di dialogo:
%1.
Pulsanti disponibili:
@@
; due termini separati da un segno Più
@msg2Piu
%1+%2
@@
; due termini separati da uno spazio
@msg2Spazi
%1 %2
@@
; riga del buffer utente per il comando Annulla
@lstEscape
Annulla. Esc, oppure Alt+A
@@
; nome reale degli script a gestione personale
@msgPersonalScript
EseguePersonali%1
@@
; base per informazioni sulle serie di tasti personali
@hlpInfoSerie
%1 da %3 a %4. %2%3-%4.
@@
; assenza di un aiuto in linea per la finestra corrente
@hlpNoInLinea
Nessun aiuto in linea per la finestra corrente.
@@
; assenza di un aiuto in linea per la finestra corrente - versione corta
@hlpNoInLinea_corto
Nessun aiuto.
@@
11.2. Le serie di script.
L'obiettivo della prima serie di script, di cui inizieremo tra poco a costruire gli elementi, sarà completare una funzionalità che avevamo già predisposto nel capitolo scorso, quella dei contrassegni numerati. Nel dettaglio, realizzeremo dapprima nove script, che registrano ciascuno una diversa posizione identificata tramite un numero, ed altri nove, i quali si occupano di tornarci in seguito.
In questa occasione, avendo noi ben diciotto script da realizzare, ci basterà crearne in realtà soltanto due, uno per quelli che registrano la posizione, ed uno per quelli che vi tornano. Una volta realizzati i due prototipi, basterà posizionarci sopra ad uno di questi e, attivando il nostro apposito script di duplicazione, sarà poi sufficiente indicare solo il numero di copie che vogliamo Jaws realizzi per noi.
Non si tratta, in ogni caso, di un semplice copia e incolla, magari un po' evoluto, bensì di una vera e propria simulazione di quanto faremmo noi se dovessimo impostare gli script a mano. In particolare, saranno replicate, per ciascuno script, l'apertura e la compilazione dell'apposita schermata con la procedura guidata. Verrà riservato alle istruzioni nel codice soltanto il salvataggio dei tasti di attivazione, con il loro abbinamento ai singoli script, tramite la compilazione del file tasti predefinito
"Default.JKM".
Come al solito, le informazioni più di dettaglio su script e funzioni, sia quelle che andremo a realizzare, sia quelle integrate di Jaws, saranno inserite nella documentazione proposta assieme a ciascun elemento di codice. Eventuali argomenti particolari, invece, saranno trattati con delle note a sé stanti, che introdurranno i blocchi di codice che li riguardano.
Inizieremo, appunto, da una presentazione dei prossimi tre elementi, che costituiscono il primo gruppo di funzioni correlate.
11.2.1. Rilevare il codice dai file script.
Lo scopo di questo primo blocco di quattro funzioni è quello espresso nel titolo: da dentro all'Editor di Script, con il cursore in un punto qualsiasi all'interno dello script da duplicare, rilevare il codice dello script, controllando che, appunto, si sia effettivamente posizionati dentro ad un elemento di codice valido, e di che tipo si tratti.
In realtà, la quasi totalità di questo compito è svolta dall'ultima funzione di questo blocco, mentre le altre tre sono funzioni accessorie a quella principale. Vale tuttavia la pena di citare la terza di queste, che serve ad estrarre la parte iniziale o finale di una stringa, a partire da, oppure fino a, un carattere o una stringa da utilizzare come separatore.
Si tratta di uno strumento molto utile, che gioca su valori predefiniti e parametri opzionali, i quali consentono di modificare una stringa in un modo ben preciso indicando come parametro anche il solo testo da elaborare. Proprio per la sua versatilità, questa funzione sarà spesso chiamata dalle funzioni e dagli script che incontreremo da qui in avanti.
Esercizio 11.2.2. La funzione ToglieTab.
FileScript. Default.JSS
Nome. ToglieTab
Descrizione. Rimuove gli eventuali caratteri di Tabulazione, Ascii 9, dalla stringa passata come parametro.
Ritorni. Di tipo String. Il testo ripulito da eventuali caratteri di tabulazione.
Parametri.
1. sTesto. La stringa da elaborare. Di tipo String.
Novità.
1. La costante "A9", che corrisponde al carattere Tabulazione, Ascii 9.
Note.
1. Questa funzione, come altre più avanti, nasce per semplificare la sostituzione di caratteri ricorrenti, così come quello di Tabulazione, che si deve effettuare con una funzione in cui il nome è piuttosto lungo già da solo.
Codice.
String Function ToglieTab (string sTesto)
Return StringReplaceSubstrings (sTesto, A9, NULLO); restituisce il testo senza caratteri di tabulazione
EndFunction
Collaudo.
1. Come spesso capita nel caso di funzioni, il collaudo dell'elemento di codice va rinviato al primo script utile. Per questo blocco, e per i prossimi, sarà chiesto soltanto di porre come opzionali i parametri che eventualmente lo prevedessero.
Esercizio 11.2.3. La funzione ImpostaPipe.
FileScript. Default.JSS
Nome. ImpostaPipe
Descrizione. Qualora non sia specificato alcun carattere o stringa come separatore, viene impostato a tale scopo il carattere Pipe, Ascii 124.
Ritorni. Di tipo Void. Nessuno.
Parametri.
1. sSeparatore. Per Riferimento. L'eventuale carattere o stringa impostati come separatore. Di tipo String.
Note.
1. Tale funzione serve a ridurre il codice che gestisce l'impostazione di un separatore predefinito, operazione che si ripeterà più volte nelle funzioni da qui in avanti.
Codice.
Void Function ImpostaPipe (string ByRef sSeparatore)
If !sSeparatore Then; se non si è indicato alcun carattere o stringa di separazione,
Let sSeparatore = PIPE; imposta il carattere Ascii 124
EndIf; fine controllo caratteri
EndFunction
Esercizio 11.2.4. La funzione TroncaTesto.
FileScript. Default.JSS
Nome. TroncaTesto
Descrizione. Mantiene, o elimina, la parte iniziale, oppure quella finale, del testo specificato, fino alla prima, o al numero indicato, di occorrenze di un carattere di separazione. Questo separatore sarà il carattere o la stringa specificata, oppure il carattere Pipe, qualora non si specifichi nulla.
Ritorni. Di tipo String. La stringa, da mantenere oppure rimuovere, fino , oppure a partire da, agli eventuali caratteri di separazione specificati.
Parametri.
1. sTesto. Il testo da elaborare. Di tipo String.
2. sSeparatore. Eventuale Carattere o stringa di separazione delle parti del testo; se non specificata, sarà utilizzato il carattere Pipe, Ascii 124. Di tipo String. Parametro Opzionale.
3. iValore. Eventuale numero delle occorrenze da mantenere o rimuovere; se non specificato, sarà elaborata la prima parte iniziale del testo, altrimenti, un valore positivo indica di contare le occorrenze dall'inizio, mentre un numero negativo indica di farlo dalla fine del testo. Di tipo Int. Parametro Opzionale.
4. iRimuovi. Se valorizzato con TRUE, indica di rimuovere le porzioni di testo indicate; se non specificato, o con valore FALSE, le mantiene. Di tipo Int. Parametro Opzionale.
Novità.
1. La nostra funzione "ImpostaPipe ()".
2. La funzione integrata "Abs", (Assoluto); Restituisce il valore assoluto di un numero, senza un eventuale segno negativo. Nel suo unico parametro va indicato il valore numerico da controllare.
3. La funzione nativa "StringReverse", (DatoTestualeInvertito); Restituisce una stringa invertendone l'ordine dei caratteri, da destra a sinistra, indicata come suo unico parametro.
4. Le funzioni integrate "StringChopLeft", (TagliaDatoTestualeASinistra) , e "StringChopRight", (TagliaDatoTestualeADestra), che rimuovono una parte del testo passato come parametro, nell'ordine a sinistra e a destra; hanno entrambe due parametri:
- Il dato testuale da modificare;
- Il numero di caratteri da rimuovere a partire, nel primo caso, dall'inizio della stringa, e nel secondo, dalla sua fine.
Fasi.
1. La prima struttura di controllo interrompe subito il flusso, qualora non sia stato specificato alcun testo come primo parametro.
2. Se invece un testo c'è, dapprima la nostra funzione "ImpostaPipe ()" controlla l'impostazione di un separatore, poi sono raccolti gli eventuali dati relativi al numero di ripetizioni del separatore da trovare, e la direzione della ricerca, utilizzando anche le funzioni native "Abs ()" e "StringReverse ()".
3. Dopo altre impostazioni preliminari, si entra nel ciclo di ricerca della stringa di separazione; qui, quando il separatore viene trovato, ma non ancora nel numero di occorrenze richieste, viene aggiornato il punto d'inizio della ricerca nel testo; se e quando si trova il numero di occorrenze desiderato, viene interrotto il ciclo e restituita la parte di testo fino al punto individuato, oppure a partire da quel punto sino alla fine del testo, a seconda che si sia indicato di conservare o di tagliare la stringa così ottenuta; per eliminare una parte del testo, saranno utilizzate le funzioni native "StringChopLeft ()" e "StringChopRight ()".
4. Se si esce dal ciclo, senza quindi aver trovato il carattere separatore, o non averlo fatto nel numero di occorrenze desiderato, viene restituito il testo nella forma originale.
Note.
1. In pratica, come accennato nella premessa, se si indica un testo nel primo parametro, il solo obbligatorio, il testo inserito sarà restituito a partire dall'inizio sino all'eventuale primo carattere Pipe.
2. Se, oltre ad indicare un testo come primo parametro, si indica un carattere o una stringa come separatore, il testo sarà troncato a partire da tale separatore.
3. Se, oltre ai primi due, nel terzo parametro si indica un valore positivo, il testo immesso sarà mantenuto fino all'occorrenza del separatore con il numero indicato, partendo dall'inizio del testo; se invece il valore indicato è preceduto da un segno Meno, il conteggio delle occorrenze a cui interrompere il testo partirà dalla fine.
4. Ancora, se si pone un qualsiasi valore positivo come quarto parametro, il testo indicato nel primo sarà tagliato, anziché mantenuto; se come terzo parametro si indica un qualsiasi valore, sarà rimosso fino al numero indicato di occorrenze del separatore attivo, o dall'inizio o dalla fine sulla base del fatto che tale valore sia positivo o negativo; Se invece non lo si indica, sarà troncata solo la prima parte del testo, fino al separatore indicato.
Codice.
String Function TroncaTesto (string sTesto, string sSeparatore, int iValore, int iRimuovi)
Var
Int i, ; contatore del ciclo
String sOrigine, ; copia del testo da cercare
Int iCerca, ; numero di occorrenze da trovare
Int iPartenza, ; punto della stringa da cui iniziare a cercare
Int iLungo, ; lunghezza della stringa di origine
Int iRipeti, ; contatore delle ripetizioni
Int iPosiz, ; punto d'interruzione della stringa
String sDato; stringa temporanea
ImpostaPipe (sSeparatore); se non lo si è specificato, imposta come separatore il carattere Pipe
If iValore >= FALSE Then; se la ricerca va fatta a partire dall'inizio,
If iValore < 1 Then; se non si sono specificati parametri, o il valore Zero,
Let iCerca = PRIMA; imposta di cercare la prima occorrenza
Else; altrimenti, se un valore positivo è specificato,
Let iCerca = iValore; lo imposta come numero di occorrenze da trovare
EndIf; fine controllo occorrenze
Let sOrigine = sTesto; duplica la stringa per la ricerca
Else; Altrimenti, se la ricerca va fatta dalla fine,
Let iCerca = Abs (iValore); porta il parametro negativo al valore assoluto
Let sOrigine = StringReverse (sTesto); inverte la stringa dove cercare
Let sSeparatore = StringReverse (sSeparatore); inverte anche la stringa cercata
EndIf; fine controllo direzione ricerca
Let iPartenza = 1; inizializza la colonna da cui partire
Let iLungo = StringLength (sOrigine); lunghezza della stringa in cui cercare
While !iPosiz && iRipeti < iCerca; continua finché i caratteri non siano stati trovati, o quando si raggiunge il numero necessario di ripetizioni
Let iRipeti = iRipeti + 1; aggiorna il contatore delle ripetizioni
Let sDato = SubString (sOrigine, iPartenza, iLungo - (iPartenza -1)); crea il segmento in cui cercare
Let iPosiz = StringContains (sDato, sSeparatore); cerca la stringa indicata
If iPosiz ; se è la stringa è stata trovata,
&& iRipeti < iCerca Then; ma ancora non si è giunti al numero di occorrenze richiesto,
Let iPartenza = iPartenza + iPosiz; sposta di un'unità in avanti il punto d'inizio del successivo segmento da controllare,
Let iPosiz = FALSE; ed azzera il valore
EndIf; fine controllo ritrovamento
EndWhile; fine ciclo ricerca
If iPosiz Then; se il ritrovamento è avvenuto,
If !iRimuovi Then; se la parte è da mantenere,
; Calcola la lunghezza della stringa da ottenere, sommando i valori rilevati
Let iPosiz = (iPosiz + iPartenza) - 2
If iValore >= FALSE Then; se la ricerca era dall'inizio,
Return StringLeft (sTesto, iPosiz); preleva la stringa da sinistra
Else; altrimenti, se era dalla fine,
Return StringRight (sTesto, iPosiz); la preleva da destra
EndIf; fine controllo porzione mantenuta
Else; altrimenti, se la parte è da eliminare,
; calcola la lunghezza della stringa da rimuovere, aggiungendo anche i caratteri del separatore
Let iPosiz = ((iPosiz + iPartenza) - 2) + StringLength (sSeparatore)
If iValore >= FALSE Then; se la ricerca era dall'inizio,
Return StringChopLeft (sTesto, iPosiz); rimuove la stringa a sinistra
Else; altrimenti, se era dalla fine,
Return StringChopRight (sTesto, iPosiz); rimuove la stringa da destra
EndIf; fine controllo porzione eliminata
EndIf; fine controllo modifica
Else; altrimenti, se il separatore non è stato trovato,
Return sTesto; restituisce la stringa originale invariata
EndIf; fine controllo esito
EndFunction
Collaudo.
1. Poiché come detto la funzione ha solo il primo come parametro obbligatorio, si invita ad aprire subito il file documentazione, "Default.JSD", per porre la riga con la dicitura ":Optional", appena prima del secondo parametro dei quattro presenti nella scheda della funzione.
Esercizio 11.2.5. La funzione CaricaScript.
FileScript. Default.JSS
Nome. CaricaScript
Descrizione. Raccoglie il codice di uno script, memorizzandone tutte le istruzioni, come opzione predefinita, oppure prelevando soltanto il contenuto senza la riga d'intestazione ed il comando di chiusura.
Ritorni. Di tipo Int. TRUE per un corretto caricamento dei dati, FALSE per un risultato nullo.
Parametri.
1. sNome. Per Riferimento. Il nome dello script. Di tipo String.
2. sCodice. Per Riferimento. Il codice dell'elemento, completo o limitato al solo contenuto tra l'intestazione ed il comando di chiusura. Di tipo String.
3. iCompleto. Se con valore positivo, indica di memorizzare tutto il contenuto del codice; se senza valore, o non specificato, rileva solo il contenuto tra l'intestazione ed il comando di chiusura. Di tipo Int. Parametro Opzionale.
Novità.
1. Le nostre funzioni "ToglieTab ()" e "TroncaTesto ()".
2. Le funzioni integrate "PriorCharacter", (CaratterePrecedente), e "NextCharacter", (CarattereSuccessivo), le quali spostano il cursore di un carattere, rispettivamente, a sinistra o a destra; entrambe sono senza parametri.
3. Le costanti "CONTROL_R" e "APERTA_TONDA", che corrispondono alle rispettive combinazioni tasti e carattere.
4. La costante "CHIAVE_SCRIPT", la quale corrisponde alla singola parola chiave che identifica questi elementi di codice, e che quindi non può essere usata come termine a sé stante in quanto già utilizzata da Jaws.
Fasi.
1. La prima struttura di controllo verifica di essere nell'Editor di Script, altrimenti il flusso viene interrotto.
2. Dopo aver spento la sintesi, si utilizza il comando "Seleziona script" del menu modifica, tramite il comando rapido della costante "CONTROL_R", per selezionare tutto il codice, intestazione e comando di chiusura compresi, ripulendo tale testo selezionato grazie alla nostra funzione "ToglieTab ()", servendosi anche di "PriorCharacter ()" e "NextCharacter ()", ed infine salvandolo in una variabile.
3. Una seconda struttura di controllo, qualora il testo rilevato non sia uno script valido, interrompe il flusso restituendo un risultato nullo. Altrimenti, il nome ed il codice dello script sono trasmessi alla funzione chiamante per riferimento, eventualmente servendosi della nostra "TroncaTesto ()", e comunque restituendo un esito positivo. In entrambi i casi, prima di uscire dalla funzione, viene riattivata la sintesi.
Note.
1. Questa è la funzione principale del blocco, di cui si accennava nella premessa. Il codice dello script viene qui raccolto usando la funzione "TroncaTesto ()" per rimuovere l'intestazione e il comando di chiusura dal codice, poiché sarà la procedura guidata di creazione, attivata dal nostro script, a predisporre l'inizio e la fine di ciascun elemento di codice inserito.
Codice.
Int Function CaricaScript (string ByRef sNome, string ByRef sCodice, int iCompleto)
If !SiamoNellaFinestra (MANAGER) Then; se non ci si trova nell'Editor di Script,
PronunciaEsegue (); ripete i tasti premuti, leggendo il nome dello script,
Return FALSE; e restituisce un risultato nullo, interrompendo il flusso
EndIf; fine controllo finestra
Var
String sTesto, ; eventuale contenuto dello script
Int iLungo, ; lunghezza della parola iniziale degli script
Int iPosiz, ; posizione di fine nome dello script
Int i; contatore del ciclo
SpeechOff (); spegne la sintesi
TypeKey (CONTROL_R); attiva la selezione di un eventuale script sul cursore
Pause ()
; salva l'eventuale contenuto selezionato, cui sono rimossi spazi in eccesso e tabulazioni
Let sTesto = TagliaSpazi (ToglieTab (GetSelectedText ()))
PriorCharacter (); si sposta di un carattere indietro, casomai per togliere la selezione
NextCharacter (); ritorna alla posizione iniziale
Let iLungo = StringLength (CHIAVE_SCRIPT); salva la lunghezza della parola d'inizio script
If !sTesto ; se nessun contenuto è stato rilevato,
|| StringLeft (sTesto, iLungo) != CHIAVE_SCRIPT Then; oppure, se non si tratta di uno script,
SpeechOn (); riattiva la sintesi
SayMessage (OT_ERROR, hlpNoScript, hlpNoScript_corto); legge l'avviso,
Return FALSE; e restituisce un risultato nullo, interrompendo il flusso
EndIf; fine controllo script
Let iPosiz = StringContains (sTesto, APERTA_TONDA); trova la fine del nome dello script
Let iLungo = iLungo + 1; aggiorna il dato di partenza
Let sNome = TagliaSpazi (SubString (sTesto, iLungo, iPosiz - iLungo)); estrae la stringa
If !iCompleto Then; se il contenuto deve escludere la prima e l'ultima riga di codice,
; rimuove le estremità individuate con il carattere di Nuova Linea, Ascii 10
Let sCodice = TroncaTesto (TroncaTesto (sTesto, LF, 1, TRUE), LF, -1, TRUE)
Else; altrimenti,
Let sCodice = sTesto; lascia la forma rilevata
EndIf; fine controllo estrazione codice
SpeechOn (); riattiva la sintesi
Return TRUE; restituisce l'esito positivo
EndFunction
Collaudo.
1. Anche qui, l'ultimo parametro opzionale consiglia di andare subito nel file di documentazione del file Predefinito, cercare la scheda della funzione "CaricaScript", e porre la speciale dicitura tra il secondo ed il terzo parametro.
11.2.6. Trovare la sezione dove sono registrati i tasti di attivazione.
Quando si crea uno script, come detto fin dai primi capitoli, i tasti di attivazione sono registrati nel file tasti relativo al file script corrente. Il suo nome è lo stesso del file script, solo con estensione ".JKM", ma la sezione di questo file, dove saranno presenti tali combinazioni una volta trascritte, può essere diversa da caso a caso.
Nella normale gestione degli script, quella realizzata da dentro l'Editor di Jaws, non è necessario conoscere in quale delle sezioni siano stati salvati i suoi tasti di attivazione. Se quello che noi stiamo creando è uno script di base per le duplicazioni, invece, la procedura di copia ha di norma la necessità di conoscere il nome di tale sezione, per potervi aggiungere direttamente tramite il codice anche i tasti di attivazione degli ulteriori script creati da Jaws al nostro posto. Questa, infatti, se da un lato è l'unica fase della procedura guidata di creazione script che non siamo riusciti a replicare, dall'altro è anche la più semplice da simulare, in quanto basterà servirsi delle normali funzioni di scrittura dati sui file "INI" utilizzate sinora.
Per conoscere il nome della sezione dove Jaws ha trascritto la combinazione da noi immessa nel prototipo, dovremo compiere un cammino al contrario, partendo dalle due informazioni in nostro possesso. Così, saremo costretti a rilevare dapprima tutte le sezioni presenti nel file tasti abbinato all'applicativo corrente, e cercare in quale di esse Jaws ha registrato la combinazione tasti da noi scelta, servendoci delle prossime quattro funzioni.
Tra queste, vanno evidenziate quelle che rilevano le sezioni e le chiavi dai file in formato "INI". Così come avevamo fatto nell'ottavo capitolo con l'Aiuto in linea, esse hanno la particolarità di avere il suffisso "Ex", (Ulteriore), con quindi un parametro in più per indicare la cartella dove prelevare i dati.
In questo caso, useremo il contenuto della costante numerica "UTENTE", di valore 6, che serve appunto ad essere certi di elaborare dati solo dalla cartella con le Impostazioni per l'Utente.
Esercizio 11.2.7. La funzione ImpostaConfigurazione.
FileScript. Default.JSS
Nome. ImpostaConfigurazione
Descrizione. Qualora il nome di archivio indicato non abbia un'estensione, vi aggiunge quella predefinita dei file configurazione.
Ritorni. Di tipo String. Il nome di archivio eventualmente completato dall'estensione predefinita.
Parametri.
1. sArchivio. Per Riferimento. Il nome dell'archivio da controllare. Di tipo String.
Note.
1. La funzione nasce solo per semplificare il codice, in questo e nei successivi casi in cui la struttura di controllo che la compone sarà utilizzata dalle funzioni chiamanti.
Codice.
String Function ImpostaConfigurazione (string ByRef sArchivio)
If PathRemoveExtension (sArchivio) == sArchivio Then; se il nome indicato è privo d'estensione,
Let sArchivio = sArchivio + JCF; aggiunge l'estensione predefinita dei file di configurazione
EndIf; fine controllo estensione
EndFunction
Esercizio 11.2.8. La funzione SezioniUtente.
FileScript. Default.JSS
Nome. SezioniUtente
Descrizione. Rileva le sezioni presenti nel file di configurazione indicato, all'interno della cartella con le Impostazioni per l'Utente.
Ritorni. Di tipo String. L'elenco delle sezioni rilevate nel file di configurazione specificato.
Parametri.
1. sArchivio. Il nome del file di configurazione da elaborare; ; qualora in tale nome non sia specificata un'estensione, viene aggiunta quella predefinita JCF. Di tipo String.
Novità.
1. La funzione integrata "IniReadSectionNamesEx", (LeggiNomiSezioniFileIniUlteriore); Rileva le sezioni del file di configurazione specificato, limitandone la ricerca ad un determinato percorso. Ha due parametri:
- Un codice numerico che indica in quale cartella cercare il file da cui leggere i dati;
- Il nome dell'archivio da elaborare.
Codice.
String Function SezioniUtente (string sArchivio)
ImpostaConfigurazione (sArchivio); se non presente, aggiunge l'estensione dei file configurazione
; Dalle impostazioni per l'Utente, restituisce le sezioni dell'archivio indicato
Return IniReadSectionNamesEx (UTENTE, sArchivio)
EndFunction
Esercizio 11.2.9. La funzione ChiaviUtente.
FileScript. Default.JSS
Nome. ChiaviUtente
Descrizione. Rileva le chiavi nella sezione e archivio specificati, all'interno della cartella delle Impostazioni per l'Utente.
Ritorni. Di tipo String. L'elenco delle chiavi lette nel file di configurazione specificato.
Parametri.
1. sArchivio. Il nome del file di configurazione da elaborare; qualora in tale nome non sia specificata un'estensione, viene aggiunta quella predefinita JCF. Di tipo String.
2. sSezione. La sezione dell'archivio in cui operare. Di tipo String.
Codice.
String Function ChiaviUtente (string sArchivio, string sSezione)
ImpostaConfigurazione (sArchivio); se non presente, aggiunge l'estensione dei file configurazione
; Dalle impostazioni per l'Utente, restituisce le chiavi di archivio e sezione indicati
Return IniReadSectionKeysEx (sSezione, UTENTE, sArchivio)
EndFunction
Esercizio 11.2.10. La funzione LeggeUtente.
FileScript. Default.JSS
Nome. LeggeUtente
Descrizione. Rileva il dato della chiave indicata, nella sezione e archivio specificati all'interno della cartella delle Impostazioni per l'Utente.
Ritorni. Di tipo String. Il dato letto dall'archivio specificato.
Parametri.
1. : sArchivio. Il nome del file di configurazione da elaborare; ; qualora in tale nome non sia specificata un'estensione, viene aggiunta l'estensione predefinita JCF. Di tipo String.
2. sSezione. La sezione dell'archivio in cui operare. Di tipo String.
3. sChiave. La chiave del dato da leggere. Di tipo String.
Codice.
String Function LeggeUtente (string sArchivio, string sSezione, string sChiave)
ImpostaConfigurazione (sArchivio); se non presente, aggiunge l'estensione dei file configurazione
; Dalle impostazioni per l'Utente, restituisce il dato di archivio, e sezione e chiave indicati
Return IniReadStringEx (sSezione, sChiave, NULLO, UTENTE, sArchivio)
EndFunction
Esercizio 11.2.11. La funzione TrovaSezione.
FileScript. Default.JSS
Nome. TrovaSezione
Descrizione. Verifica nell'archivio tasti specificato la presenza di uno script, trasmettendo per riferimento sia la sezione dove è stata eventualmente trovata l'assegnazione, sia i tasti di attivazione ad esso abbinati.
Ritorni. Di tipo Int. TRUE per i dati trovati, FALSE per il mancato rilevamento.
Parametri.
1. sFile. L'archivio in cui cercare lo script. Di tipo String.
2. sNome. Il nome dello script da cercare. Di tipo String.
3. sSezione. Per Riferimento. La sezione dove è stato rilevato lo script. Di tipo String.
4. sTasti. Per Riferimento. La combinazione tasti che attiva lo script indicato. Di tipo String.
Novità.
1. Le nostre funzioni "SezioniUtente ()" e "ChiaviUtente ()".
Fasi.
1. Tramite la nostra funzione "SezioniUtente ()", sono raccolti i dati per far partire un ciclo che scorra le sezioni del file tasti specificato.
2. Grazie all'altra funzione, "ChiaviUtente ()", si può avviare un secondo ciclo che invece scorra i tasti, o le loro combinazioni, che fungono da chiavi nelle singole sezioni.
3. Una volta trovati i tasti che sono abbinati al nome di script da cercare, i cicli s'interrompono e viene restituito il nome della sezione.
4. Se i cicli si concludono senza aver trovato lo script cercato, viene restituita una stringa vuota.
Codice.
Int Function TrovaSezione (string sFile, string sNome, ; intestazione con i primi due parametri
string ByRef sSezione, string ByRef sTasti); seconda riga con gli altri due
Var
string sSezioni, ; elenco delle sezioni
Int j, ; contatore del ciclo primario
Int k, ; contatore del secondo ciclo
String sChiavi, ; elenco delle chiavi della sezione
String sDato; dato testuale
Let sSezioni = SezioniUtente (sFile); raccoglie le sezioni dall'archivio
For j = 1 To StringSegmentCount (sSezioni, PIPE); scorre le sezioni rilevate
Let sSezione = StringSegment (sSezioni, PIPE, j); estrae la singola sezione da elaborare
Let sChiavi = ChiaviUtente (sFile, sSezione); legge i dati per l'Utente
For k = 1 To StringSegmentCount (sChiavi, PIPE); scorre le chiavi rilevate
Let sTasti = StringSegment (sChiavi, PIPE, k); estrae la combinazione tasti che funge da chiave
Let sDato = LeggeUtente (sFile, sSezione, sTasti); legge il dato
If sDato == sNome Then; se il dato estratto ed il nome di script coincidono,
Return TRUE; restituisce l'esito positivo
EndIf; fine controllo confronto
EndFor; fine ciclo chiavi
EndFor; fine ciclo sezioni
Return FALSE; se il flusso non si è interrotto prima, restituisce un risultato nullo
EndFunction
11.2.12. L'uso dei record.
Prima di affrontare il prossimo blocco di funzioni, è necessario introdurre un argomento molto importante, che caratterizzerà gran parte del nostro lavoro da qui in avanti: la gestione di informazioni diverse in una stessa riga di dati.
Per farne subito un esempio concreto, riproponiamo di seguito un elenco di saluti, separati tra loro dal carattere Pipe, che avevamo incontrato nel quinto e nell'ottavo capitolo:
Buongiorno|Buon pomeriggio|Buonasera|Buonanotte
Questo breve elenco, composto da quattro parti coincidenti con gli altrettanti diversi saluti, lo avevamo analizzato sinora come tale, prima estraendo un termine da leggere, poi cercandone uno per averne restituita la posizione nell'elenco stesso. Se questo stesso elenco lo scrivessimo in un file in formato "INI", dovremmo utilizzare il seguente schema:
[Sezione]
Chiave=Buongiorno|Buon pomeriggio|Buonasera|Buonanotte
In questa forma, il nostro elenco diventerebbe quello che sinora abbiamo chiamato "dato", in quanto contenuto letto tramite le apposite funzioni. Abbiamo però già analizzato che in esso le informazioni inserite non sono soltanto una, bensì quattro, ciascuna utilizzabile in modo a sé stante.
Nell'attesa di poter affrontare questa distinzione sotto altri aspetti, per il momento basterà dire che l'intera riga, composta dalle due parti separate dal segno Uguale, la potremmo rinominare in questo modo:
Record=Campo1|Campo2|Campo3|Campo4
Quindi, tutta la riga la possiamo chiamare "record", e ad ogni singolo dato, potremo dare il nome convenzionale di "campo", che in questo esempio abbiamo diversificato aggiungendo a ciascuno un suffisso numerico progressivo.
Da qui in avanti, quando avremo bisogno di scrivere o leggere il contenuto dei vari dati, eventualmente inseriti in un'unica stringa composta da più segmenti, cercheremo di usare questa terminologia, "record" e "campo", anche per poi capire meglio cosa si sta facendo.
In modo estensivo, con "record" potremo intendere anche solo una stringa costituita da più "campi" divisi da un carattere, o una stringa, di separazione, a prescindere che essa provenga da un file in formato "INI". In qualche caso tali speciali stringhe, continueremo a chiamarle "elenchi", specialmente quando si tratterà di voci da proporre nelle finestre di dialogo.
Il prossimo blocco di elementi di codice sarà composto da cinque funzioni, seguite dallo script di creazione e dai due prototipi che realizzeremo per duplicarli. Di queste funzioni, la prima costituisce un altro strumento molto efficace, tramite il quale potremo creare o aggiornare dei "record", sostituendo o aggiungendo dei campi al loro interno. Anche in questo caso, così come è stato per la funzione "TroncaTesto ()", sfrutteremo i parametri opzionali per poter indicare il tipo di lavoro da svolgere.
L'altra funzione da citare è quella che si occupa soltanto di spostare il cursore da un controllo all'altro, e da un campo d'inserimento all'altro, dentro alla procedura guidata di creazione script. La sua esistenza è dovuta, oltre al risparmio di codice, anche al tentativo di contribuire ad una maggiore visibilità dello stesso, sulla base del notevole numero di chiamate della funzione necessarie allo script.
Esercizio 11.2.13. La funzione CambiaCampo.
FileScript. Default.JSS
Nome. CambiaCampo
Descrizione. Sovrascrive, Aggiunge o rimuove un campo di un record, sulla base dei parametri specificati.
Ritorni. Di tipo String. Il record con i campi eventualmente rimasti dopo le modifiche.
Parametri.
1. sRecord. Il record cui sottoporre l'azione di modifica. Di tipo String.
2. sTermine. L'eventuale contenuto del campo da rimuovere, qualora non si specifichino altri parametri, oppure da aggiungere o integrare al record indicato nel primo parametro, sulla base del valore numerico specificato nel parametro successivo; se si desidera eliminare dal record iniziale il campo di cui si indica un numero progressivo, in questo secondo parametro deve essere posto un testo vuoto. Di tipo String.
3. iPosiz. Nel caso in cui il secondo parametro contenga un qualche dato testuale, il valore qui inserito indica il campo del record che va aggiornato con tale termine; sempre nel caso precedente, se si immette un valore -1, oppure la costante ULTIMA, il termine indicato sarà comunque aggiunto in coda al record; nel caso in cui il termine sia vuoto, ed il valore sia a -1, sarà aggiunto il separatore impostato ; ancora, se il secondo parametro è vuoto, un valore positivo indica il campo che deve essere eliminato; infine, se non lo si specifica, il termine presente nel secondo parametro sarà cercato per rimuovere il campo che lo contiene nel record. Di tipo Int. Parametro Opzionale.
4. sSeparatore. L'eventuale carattere, o stringa, che serve da separatore dei campi nel record; se non viene specificato, sarà usato il carattere Pipe, Ascii 124. Di tipo String. Parametro Opzionale.
Fasi.
1. In una prima ed articolata struttura di controllo, con vari livelli annidati al suo interno, si elaborano i parametri specificati; durante questi controlli, qualora i parametri non siano stati immessi in numero sufficiente, il flusso viene interrotto.
2. Nell'altra struttura di controllo della funzione, si restituisce il record eventualmente modificato, sulla base di quanto rilevato in precedenza.
3. Se la seconda struttura non intercetta il flusso, il record viene restituito nella forma originale oppure con il solo termine indicato come secondo parametro a costituire un record.
Note.
1. Se si indica il numero minimo di parametri, dal record posto come primo parametro sarà rimosso il campo il cui contenuto corrisponde al testo specificato come secondo parametro.
2. Se invece si specificano il primo parametro, con il record, ed il terzo, quello con il valore numerico, sarà rimosso dal record il campo corrispondente al numero progressivo indicato; in questa soluzione, è possibile indicare solo valori positivi, poiché numeri preceduti dal segno Meno sarebbero ignorati, lasciando così invariato il record.
3. Se invece sono specificati tutti e tre i primi parametri, il contenuto del secondo andrà a sostituire il campo nel record con il numero progressivo indicato nel terzo parametro; qualora il valore sia negativo, ad esempio -1, oppure sia superiore al numero di campi presenti nel record, il contenuto del secondo parametro sarà aggiunto alla fine del record.
4. A prescindere dal fatto che si specifichino solo due, o tutti tre, i primi parametri, il carattere o la stringa posta come quarto parametro sarà utilizzata come separatore dei campi nel record, in alternativa al carattere Pipe, Ascii 124, che viene usato quando tale parametro non viene specificato.
Codice.
String Function CambiaCampo (string sRecord, string sTermine, int iPosiz, string sSeparatore)
Var
Int iLungo, ; lunghezza del separatore
Int iCampi, ; numero dei campi rilevati nel record
String sDato; dato temporaneo
ImpostaPipe (sSeparatore); se non lo si è specificato, imposta come separatore il carattere Pipe
Let iLungo = StringLength (sSeparatore); conta i caratteri del separatore
Let iCampi = StringSegmentCount (sRecord, sSeparatore); conta i campi presenti nel record
If !iPosiz Then; se il numero dell'campo da elaborare non è specificato,
If !sTermine Then; se non lo è nemmeno la stringa da cercare,
Return sRecord; restituisce la forma originale
Else; altrimenti, se la stringa è indicata,
Let iPosiz = StringSegmentIndex (sRecord, sSeparatore, sTermine, TRUE); cerca la stringa nei campi
If !iPosiz Then; se l'campo non è stato individuato,
Return sRecord; restituisce la forma originale
EndIf; fine controllo campo da rimuovere
EndIf; fine controllo valore numerico
ElIf sTermine Then; se è presente anche il dato da integrare nei campi,
If iPosiz < FALSE Then; se si è specificato di aggiungere il termine in coda,
Let iPosiz = StringSegmentCount (sRecord, sSeparatore) + 1; aggiunge un'unità al numero di campi esistenti
EndIf; fine controllo valore negativo
If iPosiz == PRIMA Then; se il campo da sostituire è il primo,
If iCampi <= PRIMA Then; se anche i campi non sono superiori ad uno,
Let sDato = sTermine; duplica solo la stringa specificata
Else; altrimenti, se ve ne sono più d'uno,
Let sDato = sTermine + sSeparatore; aggiunge il separatore in coda alla stringa specificata
EndIf; fine controllo numero campi
Else; altrimenti, se il dato è da inserire o aggiungere in coda,
Let sDato = sSeparatore + sTermine; aggiunge il separatore all'inizio
EndIf; fine controllo aggiunta separatore
EndIf; fine controllo posizione campo
If !sTermine ; se nessun termine è stato specificato,
&& iPosiz == ULTIMA Then; ed è stato indicato di aggiungere un campo alla fine,
Return FormatString (msg2, sRecord, sSeparatore); aggiunge il solo separatore senza dato
ElIf iCampi > PRIMA ; se i dati sono più d'uno,
|| iPosiz > PRIMA Then; oppure, se il campo da elaborare è maggiore di uno,
If iPosiz == PRIMA Then; se quello da aggiornare o rimuovere è il primo campo,
; compone il dato, casomai aggiungendovi il resto del record dal secondo campo in poi
Return FormatString (msg2, sDato, TroncaTesto (sRecord, sSeparatore, PRIMA, TRUE))
ElIf iCampi == iPosiz Then; se il numero di dati e quello dell'campo da elaborare sono uguali,
; compone il dato tagliando dal record l'ultimo campo, e casomai accodandovi il dato
Return FormatString (msg2, TroncaTesto (sRecord, sSeparatore, ULTIMA, TRUE), sDato);
ElIf iPosiz > iCampi Then; se il nuovo dato va posto dopo quelli esistenti,
; compone il dato, casomai accodandolo all'elenco attuale
Return FormatString (msg2, sRecord, sDato);
Else; altrimenti, se l'campo da sostituire si trova in mezzo ad altri campi,
; inserisce il dato tra i campi fino a quello precedente al numero da sostituire e la restante parte del record
Return FormatString (msg4, TroncaTesto (sRecord, sSeparatore, iPosiz - 1), sDato, sSeparatore, TroncaTesto (sRecord, sSeparatore, iPosiz - iCampi))
EndIf; fine controlli composizione dato
EndIf; fine aggiornamento dato
Return sDato; restituisce il termine aggiunto, o una stringa vuota
EndFunction
Collaudo.
1. Essendoci due parametri opzionali, si invita ad aggiornarne l'impostazione dentro al file di documentazione predefinito, ponendo la speciale stringa tra il secondo e terzo parametro della scheda.
Esercizio 11.2.14. La funzione ElencoNumeri.
FileScript. Default.JSS
Nome. ElencoNumeri
Descrizione. Crea un elenco di scelte numeriche, in forma testuale, che parte dal valore indicato e termina con quello predefinito nella variabile MAX_VOCI.
Ritorni. Di tipo String. Un elenco di cifre in forma testuale, a partire dal valore impostato sino a quello massimo consentito.
Parametri.
1. iPartenza. Valore da cui far partire l'elenco. Di tipo Int.
Novità.
1. La nostra funzione "CambiaCampo ()".
2. La costante "MAX_VOCI", che equivale al numero 99.
Note.
1. All'interno del ciclo, che costituisce in pratica il codice della funzione, l'utilizzo della nostra funzione "CambiaCampo ()" consente di costruire da zero l'elenco delle voci da visualizzare.
Codice.
String Function ElencoNumeri (int iPartenza)
Var
Int i, ; contatore del ciclo
String sElenco; stringa da restituire
For i = iPartenza + 1 To MAX_VOCI; crea un elenco di valori in forma testuale
Let sElenco = CambiaCampo (sElenco, IntToString (i), ULTIMA); aggiunge il valore del ciclo
EndFor; fine ciclo creazione elenco
Return sElenco; restituisce l'elenco di voci testuali creato
EndFunction
Collaudo.
Esercizio 11.2.15. La funzione SceltaValore.
FileScript. Default.JSS
Nome. SceltaValore
Descrizione. Consente di scegliere il valore corrispondente ad una delle voci proposte nell'elenco della finestra di dialogo.
Ritorni. Di tipo Int. Il valore scelto.
Parametri.
1. sElenco. L'elenco delle voci di scelta. Di tipo String.
2. sTitolo. Il titolo della finestra di dialogo. Di tipo String.
3. iPartenza. Il numero della voce su cui ci si posiziona all'ingresso. Di tipo Int. Parametro Opzionale.
Fasi.
1. Dopo aver comunque impostato il valore di controllo, si entra in un ciclo dove viene proposto di scegliere un valore tra quelli proposti.
2. Se si opera una scelta, viene annullato il valore di controllo per l'uscita dal ciclo, e quindi viene restituito il valore scelto.
3. Se invece si abbandona la scelta, viene proposta una conferma all'uscita: se la si conferma, si esce dal ciclo e viene restituito un risultato nullo, altrimenti si ritorna alla precedente finestra di scelta.
Note.
1. Per quanto riguarda la funzione speciale di Jaws "DlgSelectItemInList ()", più volte utilizzata sinora con soli tre parametri, in questa occasione ne useremo un quarto, che serve a stabilire la voce selezionata all'ingresso nella finestra di dialogo; più avanti concluderemo l'esame delle sue possibilità, illustrando anche gli ultimi due parametri facoltativi.
Codice.
Int Function SceltaValore (string sElenco, string sTitolo, int iPartenza)
Var Int iScelta; valore della scelta effettuata
If !iPartenza Then; se il valore d'ingresso non è stato indicato,
Let iPartenza = PRIMA; imposta la voce di scelta iniziale
EndIf; fine controllo voce all'ingresso
While iPartenza; ripropone la richiesta finché un valore è impostato
Let iScelta = DlgSelectItemInList (sElenco, sTitolo, FALSE, iPartenza); consente la scelta
If iScelta Then; se è stata effettuata una scelta,
Let iPartenza = FALSE; annulla il valore per l'uscita dal ciclo
Else; altrimenti, viene chiesta l'uscita dalla fase
If ChiedeConferma (ttlConfermaUscita, msgConferma) Then; se tale uscita è stata confermata,
Let iPartenza = FALSE; annulla il valore per l'uscita dal ciclo
EndIf; fine controllo conferma
EndIf; fine controllo scelta effettuata
EndWhile; fine del ciclo di scelta
Return iScelta; restituisce la scelta operata
EndFunction
Per ulteriori spiegazioni, scrivere a:
Abramo Volpato, oppure, a: Nunziante Esposito