Programmiamoli da Soli!
Lo strumento di ricerca.
Proseguendo ad esaminare i modi per far lavorare Jaws al nostro posto, dedicheremo questo capitolo alla costruzione di un sistema che ci consenta di cercare rapidamente delle stringhe testuali appositamente predisposte, che chiameremo anche
"Elementi di ricerca"
. Ci serviremo di un vero e proprio strumento, che scorrerà per noi avanti ed indietro i documenti, fermandosi quando troverà quel che cerchiamo.
Rispetto alla classica ricerca, che si attiva digitando di volta in volta le stringhe da trovare, il nostro sistema ci consentirà di limitarci a premere delle combinazioni tasti. Sarà possibile sia cercare le singole stringhe, eseguendo da uno a nove script in serie, sia effettuare una ricerca di tutte le stringhe assieme, ed il cursore si fermerà in quel caso alla prima occorrenza di una qualsiasi stringa nell’elenco.
Potremo Inoltre usare ciascun gruppo di stringhe in tutti i file con una certa estensione, non sulla base del singolo applicativo, così come sono di solito gli script. Questo ci consentirà, quindi, di poter agire sui comandi di ricerca di una stessa stringa, relativa ad esempio al contenuto dei file
.TXT
, sia che li si apra con il Blocco Note, sia che ci si trovi in Microsoft Word o Wordpad.
Forse, a sentirla così, l’utilità del sistema può non apparire così evidente. Come al solito, potremo essere più chiari quando costruiremo gli script e le funzioni necessarie, in questo e nei prossimi capitoli. Intanto, procediamo con la consueta attività iniziale del nostro lavoro.
Esercizio 12.1.1. Aggiornare il file Personale delle Costanti.
ELEMENTO = "Elemento", ; termine omonimo
MACRO = "Macro", ; termine omonimo
SCRIPTS = "Scripts", ; termine omonimo
ARCHIVIO = "Archivio", ; termine omonimo
ELABORA = "ELABORA", ; termine omonimo
TIPO = "Tipo", ; termine omonimo
CAMPO = "Campo", ; termine omonimo
UGUALE = "=", ; carattere omonimo
VOCI = "Voci", ; termine omonimo
NUMERICO = "Numerico", ; termine omonimo
INFO = "Info", ; termine omonimo
QUARTA = 4, ; valore per la posizione omonima
MULTIPLE = "Multiple", ; termine omonimo
CONFERMA = "Conferma", ; termine omonimo
CHIAMA ="Chiama", ; termine omonimo
_E = "e", ; carattere omonimo
_I = "i", ; carattere omonimo
RN ="\r\n", ; Caratteri di ritorno a capo standard, codici Ascii 13 e 10
SPV = "[!!]", ; separatore delle voci in una stringa
INGRESSO_CAMPO = "Boink2", ; suono di ingresso in un campo di editazione
USCITA_CAMPO = "Boink1", ; suono di uscita da un campo di editazione
VIRGOLA_SPAZIO =", ", ; carattere omonimo seguito da uno spazio
UNO = "1", ; cifra omonima in forma testuale
CATTURA = 1, ; valore per cattura tasti di attivazione
AGGIUNGE = "Aggiunge", ; termine omonimo
AVANTI = 1, ; valore per indicare un avanzamento
RICERCA_INTERNA = "&", ; carattere che identifica un elemento da cercare all’interno delle righe di testo
INIZIO = 3, ; valore per Inizio di un elemento
INTERNO = 4, ; valore per tipo di ricerca
COLONNA = "Colonna", ; termine omonimo
ARRIVO = "Click1", ; avviso di fine ricerca o spostamento
INDIETRO = 2, ; valore per indicare l’arretramento
SCORRE_RIGHE = "ScorreRighe", ; nome della funzione omologa
MAX_RICERCA = 3000, ; numero massimo di millesimi di secondo che può durare una ricerca
ESC = "Escape", ; termine esteso inglese
MAX_RIGHE = 10, ; numero massimo di righe consecutive uguali
COMMENTO = "Commento", ; termine omonimo
PUNTO_VIRGOLA = ";", ; carattere omonimo
SINGOLO = "Singolo", ; termine omonimo
ESEGUE = "Esegue", ; termine omonimo
MUOVE = "Muove", ; termine omonimo
METTE = "Mette", ; termine omonimo
Esercizio 12.1.2. Aggiornare il file Personale delle Variabili Globali.
String gsCategoria, ; ultima categoria specificata come parametro
Int gnTipo, ; tipologia di procedura
Int gnMaxVoci, ; numero massimo di voci da elaborare per la procedura
Int gnNumerico, ; determina la presenza di un prefisso numerico davanti alle voci di scelta
String gsOggetto, ; oggetto a cui si rivolge l’azione
int gnMultiple, ; indicatore di immissione testo su più righe
Int gnConferma, ; attivazione della conferma all’uscita dalla procedura
String gsArchivio, ; il nome, senza estensione, dell’archivio da elaborare
String gsChiave, ; chiave del dato attivo
String gsAttiva, ; voce selezionata nella schermata principale
String gsElenco, ; elenco degli elementi da ricercare
Int gnTotali, ; numero degli elementi in un elenco
Int gnSalta, ; indica il salto della fase di scelta voce
Int gnStato, ; stato di attivazione della sintesi
Int gnMaxCampi, ; numero complessivo delle voci nella procedura
Int gnFerma, ; indica di interrompere la funzione corrente per richiamarla in seguito
Int gnRilevamento, ; indicatore della modalità di lettura della posizione
Int gnPrimo, ; indicatore di inizio
Int gnValore, ; valore temporaneo globale
Int gnProgrammato, ; indicatore dell’avvenuta programmazione di un avvio di funzione
Int gnVerso, ; indicatore della direzione di un movimento
String gsRiga, ; testo della riga corrente
String gsPrecedente, ; contenuto testuale precedente
Int gnRipeti, ; numero di ripetizioni
Esercizio 12.1.3. Aggiornare il file Personale dei Messaggi.
; voci per scelta della tipologia delle procedure guidate
@lstTipoGuidata
Cerca nel documento, attivando script con suffisso numerico, e con doppia opzione successivo o precedente.
Scrive nel documento corrente, attivando script con suffisso numerico.
Esegue nell’applicazione corrente, attivando script con suffisso numerico.
@@
;Titolo per scelta del tipo di procedura guidata
@msgTipoGuidata
Scegliere il tipo di procedura guidata
@@
; elenco dei tasti consentiti per il campo Azioni degli script a gestione personale
@lstArchivioTasti
F1|F2|F3|F4|F5|F6|F7|F8|F9|F10|F11|F12|Alt|Control|Shift|Windows|Enter
@@
; elenco di cifre da 1 a 9
@lstCifre1_9
1|2|3|4|5|6|7|8|9
@@
; prefisso per titolo di scelta dei campi nelle procedure guidate
@msgCampiGuidate
Scegliere quanti campi prevede la procedura di
@@
; mancata scrittura sui file configurazione
@hlpNoScriveJCF
Impossibile scrivere sul file %1.JCF.
@@
; titoli per le finestre di dialogo nelle procedure guidate
@lstTitoliGuidate
Selezionare l’elemento da elaborare, e Cliccare sull’azione da compiere|Avvia ricerca. Ok, oppure Alt+O.
Selezionare la %1 da elaborare, e cliccare sull’azione da compiere|Inserisce Macro. Ok, oppure Alt+O.
Selezionare lo script da elaborare, e cliccare sull’azione da compiere|Esegue script. Ok, oppure Alt+O
Modifica i Tasti per attivare %1|Campo successivo. Ok, oppure Alt+O
Modifica il Sommario di %1|Campo successivo. Ok, oppure Alt+O
Modifica le Azioni da compiere tramite %1|Campo successivo. Ok, oppure Alt+O
Selezionare l’ambito in cui far agire %1, e cliccare sull’azione da compiere|Conferma scelta. Ok, oppure Alt+O
@@
; seconda parte delle informazioni da proporre nelle fasi di inserimento o modifica testo
@lstInfoGuidate
%1re l’elemento in posizione %2.
%1re La Macro con il numero %2.
%1re il comando per lo script %2.
@@
; elenco prefissi funzioni per conclusione delle procedure guidate
@lstChiamaGuidate
Muove
Mette
Esegue
Cattura
@@
; elenco pulsanti per le procedure guidate di ricerca e scrittura nel documento corrente
@lstPulsantiGuidate1
Pulsante1=&Modifica
Pulsante2=Agg&iunge||8
Pulsante3=E&limina
Pulsante4=Sposta &giù|1|8
Pulsante5=Sposta &su|2
@@
; elenco pulsanti per la prima schermata dell’elaborazione su più campi
@lstPulsantiGuidata2
Pulsante1=&Modifica nome
Pulsante2=Agg&iunge||98
Pulsante3=&Duplica dall’elenco
Pulsante4=E&limina
Pulsante5=Campo s&uccessivo
@@
; elenco pulsanti per l’ultima schermata nella elaborazione su più campi
@lstPulsantiGuidata4
Pulsante1=Campo p&recedente
Pulsante2=&Torna all’inizio
@@
; elenco pulsanti per le schermate dalla seconda alla penultima nella elaborazione su più campi
@lstPulsantiGuidata3
Pulsante1=&Modifica
Pulsante2=Campo s&uccessivo
Pulsante3=Campo p&recedente
Pulsante4=&Torna all’inizio
@@
; due termini separati dal carattere Controbarra
@msg2Controbarra
%1\%2
@@
; elenco tipi di suffisso
@lstTipiDato
estensione|estensione|applicazione
@@
; assenza di elementi del tipo elaborato
@ttlNoElementi
Nessun%1 %2 per l’%3 %4.
@@
; prefisso per voce a righe multiple
@msgRigheMultiple
Control+Invio nuova riga.
@@
; base per messaggio nella finestra d’immissione testo
@msgMettiTesto
%1 Invio conferma, Esc annulla.
@@
; titolo mancata modifica di una voce
@ttlNoModifica
Nessuna modifica effettuata.
@@
; titolo per dato già presente
@ttlPresenzaDato
%1 %2 già presente.
@@
; invito ad immettere un nuovo testo in un campo d’inserimento
@msgAggiornaTesto
Modificare il testo immesso.
@@
; titolo per errore sulla presenza del carattere Pipe in un testo
@ttlNoPipe
Impossibile immettere un carattere Pipe, Ascii 124.
@@
; titolo sull’archiviazione dei tasti usati nelle azioni degli script a gestione personale
@ttlArchiviaTasti
Tra le azioni immesse, non si è riconosciuto %1.
@@
; conferma dei dati immessi
@msgOkDati
Confermate i dati immessi?
@@
; elenco di prefissi per immissioni o modifica testo
@lstPrefissiTesto
l’|la |il nome dello ||il |le |
@@
; elenco di suffissi per immissioni o modifica testo
@lstSuffissiTesto
in posizione %1|numero %1|||dello script %1|compiute dallo script %1
@@
; base per dato da aggiungere o modificare
@ttlInputDato
%1re %2%3 %4.
@@
; errore personalizzabile sui dati
@hlpErroreDato
Impossibile %1re il dato richiesto.
@@
; spostamento all’inizio del documento aperto
@hlpInizioFile
Inizio del documento.
@@
; spostamento all’inizio del documento aperto - versione corta
@hlpInizioFile_corto
Inizio.
@@
; pressione di Escape per interrompere
@hlpPremiEsc
Premere Escape per interrompere...
@@
; percentuale
@msgPercento
%1 percento
@@
; ricerca elementi interrotta
@msgStopRicerca
Ricerca interrotta
@@
; estensione del documento corrente non rilevata
@hlpNoEst
Nessuna estensione rilevata nel documento corrente.
@@
; estensione del documento corrente non rilevata - versione corta
@hlpNoEst_corto
Nessuna estensione.
@@
***
Gli archivi di configurazione.
Essendo tutto il capitolo dedicato ad un’unica procedura, abbiamo suddiviso le funzioni che la compongono nei blocchi di quelle che sono chiamate dal principale elemento di codice. Così, il primo di questi riguarda la creazione degli archivi dove andremo a registrare i dati necessari, non solo relativi alla procedura trattata in questo capitolo, ma pure a quelle di cui tratteremo da qui in avanti.
Si è preferito far generare tali archivi tramite degli script, nonostante siano in un normale file di testo, per una serie di motivi:
- Se ne può aggiornare facilmente il contenuto di base, secondo le personali preferenze.
- Saranno sempre collocati nella giusta cartella.
- Se fossero per errore cancellati, alla prima chiamata essi saranno ripristinati alla loro versione predefinita.
- Per quanto al punto precedente, nel caso di qualche modifica manuale al contenuto degli archivi che generasse degli errori, sarà quindi sufficiente cancellarli per riportarli al formato iniziale.
Gran parte dei dati trascritti negli archivi di configurazione sono stati impostati tramite delle assegnazioni nei file personali dei Messaggi, che così possono essere facilmente aggiornati e modificati. Per eventuali altre procedure, non previste da queste pagine, il sistema consente comunque di poterle implementare, proponendo delle schermate in cui scegliere i valori necessari al loro funzionamento.
Anche in questo caso, come nel capitolo scorso, le funzioni saranno predisposte per i compiti attuali e futuri. Così, dopo aver accennato degli script a gestione personale, qui inizieremo ad usare i termini
"Elemento"
, e
"Macro"
, che corrispondono ad altrettante categorie di procedura.
Il primo dei due termini indica proprio l’argomento del capitolo, dove
elemento di ricerca
è come detto il nome dato alle stringhe di testo predeterminate. Il secondo termine,
"Macro"
, l’abbiamo già incontrato in particolare nel decimo capitolo, come parte centrale del nome di una coppia di speciali script, e di esso tratteremo nel dettaglio più avanti.
Esercizio 12.2.1. La funzione ConverteInBell.
FileScript.
Default.JSS
Nome.
ConverteInBell
Descrizione.
Converte il carattere o la stringa indicati nel carattere Bell, Ascii 7.
Ritorni.
Di tipo String. Il testo posto come primo parametro, eventualmente convertito.
Parametri.
- sTesto. La stringa in cui sostituire i caratteri indicati come secondo parametro. Di tipo String.
- sCarattere. Il carattere, o la stringa, da sostituire con il carattere Bell, Ascii 7. Di tipo String.
Note.
- Anche questa funzione serve soltanto per semplificare la sintassi delle sostituzioni di testo.
Codice.
String Function ConverteInBell (string sTesto, string sCarattere)
Return StringReplaceSubstrings (sTesto, sCarattere, BELL); restituisce il testo convertito
EndFunction
Collaudo.
- Come al solito, per tutto questo capitolo, si dovrà compilare ogni singolo script e, casomai, porre come facoltativi alcuni parametri. Dando per scontata la prima azione, in questa parte delle schede ricorderemo ogni volta solo la necessità di realizzare la seconda.
Esercizio 12.2.2. La funzione ConfigurazioneCampi.
FileScript.
Default.JSS
Nome.
ConfigurazioneCampi
Descrizione.
Consente di impostare il file configurazione con le fasi delle procedure guidate.
Ritorni.
Di tipo Int. TRUE per la creazione delle fasi, FALSE per il suo abbandono.
Novità.
- Le nostre funzioni
ConverteInBell (),
ScriveRecord ()
e
ScriveNumerico (). - Le costanti
ELEMENTO,
MACRO,
SCRIPTS,
ARCHIVIO,
ELABORA,
TIPO,
CAMPO,
VOCI,
NUMERICO,
INFO,
MULTIPLE,
CONFERMA
e
CHIAMA
, tutte equivalenti all’omonimo termine letterale. - La costante
UGUALE
, che equivale all’omonimo carattere. - La costante
QUARTA
, che equivale al valore numerico quattro.
Fasi.
- Una prima struttura di controllo imposta il valore del tipo di procedura sulla base del nome assegnato alla categoria della procedura stessa; qualora tale categoria non fosse riconosciuta, consente di selezionare manualmente il tipo di quest’ultima, utilizzando anche la nostra funzione
ConverteInBell ()
; se tale scelta viene annullata, il flusso sarà interrotto. - La seconda struttura di controllo imposta il numero di campi trattati dalla procedura, sulla base del tipo appena definito; se si devono trattare script a gestione personale, un ciclo crea l’archivio base dei nomi dei tasti validi per le azioni svolte dagli script, servendosi della nostra
ScriveNumerico ()
; anche in questo caso, qualora la categoria non avesse abbinato un numero di campi predefinito, si consente di sceglierlo da un elenco di voci numeriche e, se la fase di scelta viene annullata, il flusso s’interrompe. - Se nella seconda struttura è stato impostato un nome di archivio particolare, dove la procedura dovrà andare ad elaborare i dati, tale nome viene trascritto nella sezione iniziale del file di configurazione, tramite la nostra
ScriveRecord (). - In ogni caso, viene effettuata una prova di scrittura, inviando all’archivio il valore del tipo di procedura rilevata; se la scrittura fallisce, ed i tasti di attivazione sono stati premuti due volte velocemente, viene pronunciato un avviso; In ogni caso,il flusso viene interrotto.
- Da qui in avanti, viene compilato il file di configurazione, prelevando le varie assegnazioni impostate nel file personale dei Messaggi, e sulla base delle impostazioni definite nelle precedenti strutture di controllo.
Note.
- Questa funzione, citata nella premessa, si rende necessaria per predisporre l’ambiente in cui alcune speciali procedure dovranno agire; il suo compito sarà quello di creare, o ripristinare, il file di configurazione base da dove saranno ricavati i dati necessari.
- Essa, in quanto priva di parametri, rappresenta un buon esempio dell’utilizzo delle variabili globali, che servono soprattutto a mettere a disposizione di tutti gli elementi di codice i dati elaborati al suo interno.
Codice.
Int Function ConfigurazioneCampi ()
Var
Int iCampi, ; numero dei campi del record
Int i, ; contatore del ciclo
String sNome, ; nome dell’archivio di configurazione
String sTitoli, ; titoli delle finestre di dialogo
String sPulsanti, ; elenco delle impostazioni relative
String sInfo, ; informazioni per le fasi d’aggiunta o modifica testo
String sPrefissi, ; parti iniziali dei nomi di funzioni
Int j, ; contatore del ciclo iniziale
String sSezione, ; etichetta della sezione in cui scrivere
Int k, ; contatore del secondo ciclo
String sDato; singolo dato estratto
If gsCategoria == ELEMENTO Then; se la procedura deve scegliere elementi di ricerca,
Let gnTipo = PRIMA; imposta la scelta iniziale
ElIf gsCategoria == MACRO Then; se invece sono da scegliere i testi delle Macro,
Let gnTipo = SECONDA; imposta l’opzione alternativa
ElIf gsCategoria == SCRIPTS Then; se, ancora, sono da elaborare gli script a gestione personale,
Let gnTipo = TERZA
Else; altrimenti, se la categoria non è stata riconosciuta, consente di sceglierne il tipo
Let gnTipo = SceltaValore (ConverteInBell (lstTipoGuidata, LF), msgTipoGuidata)
If !gnTipo Then; se non si è effettuata alcuna scelta,
Return; interrompe il flusso
EndIf; fine controllo esito scelta
EndIf; fine controllo tipo procedura
If gnTipo <= SECONDA Then; se si è impostata una delle prime due scelte,
Let iCampi = 1; imposta un unico campo per il record
Let sNome = PS_FILE; imposta il nome dell’archivio di configurazione personale
Let gnMaxVoci = MAX_SEGNI; imposta le voci di scelta da 1 a 9
Let gnNumerico = TRUE; imposta la presenza di un prefisso numerico davanti alle voci
Else; altrimenti, se è infine impostato il terzo tipo di procedura,
If gsCategoria == SCRIPTS Then; se sono da elaborare gli script a gestione personale,
Let iCampi = 5; imposta i campi necessari
For i = 1 To StringSegmentCount (lstArchivioTasti, PIPE); scorre i tasti predefiniti come validi
; trascrive i tasti, predisposti nell’apposita sezione del file di configurazione personale
ScriveNumerico (PS_FILE, ARCHIVIO + TASTI, StringSegment (lstArchivioTasti, PIPE, i), TRUE)
EndFor; fine scansione tasti
Else; altrimenti, se la categoria è da definire,
Let iCampi = SceltaValore (lstCifre1_9, FormatString (ttlSelezione,msgCampiGuidate, gsOggetto, gsCategoria))
If !iCampi Then; se non è stata operata nessuna scelta,
Return; interrompe il flusso
EndIf; fine controllo scelta
EndIf; fine controllo impostazione campo
Let sNome = NULLO; resetta il dato per far impostare il file per l’applicazione
Let gnNumerico = FALSE; lascia le voci di scelta senza alcun prefisso numerico
EndIf; fine controllo tipo impostato
If sNome Then; se il nome dell’archivio di configurazione è stato impostato,
ScriveRecord (gsCategoria, ELABORA, ARCHIVIO, sNome); inizializza il file configurazione
EndIf; fine controllo nome archivio
If !ScriveNumerico (gsCategoria, ELABORA, TIPO, gnTipo) Then; se la scrittura fallisce,
If IsSameScript () Then; se i tasti di attivazione sono stati premuti due volte,
SayFormattedMessage (OT_ERROR, hlpNoScriveJCF, NULLO, gsCategoria); pronuncia l’avviso
EndIf; fine controllo doppia pressione
Return; in ogni caso, interrompe il flusso
EndIf; fine controllo scrittura
; Converte i dati in un elenco, dalla forma su più righe, per i seguenti tipi di dati:
Let sTitoli = ConverteInBell (lstTitoliGuidate, LF); titoli delle finestre di dialogo
Let sInfo = ConverteInBell (lstInfoGuidate, LF); informazioni per le fasi di immissione o modifica testo
Let sPrefissi = ConverteInBell (lstChiamaGuidate, LF); prefissi dei nomi delle funzioni da chiamare
For j = 1 To iCampi; scorre il numero di campi scelto
Let sSezione = CAMPO + IntToString (j); fissa l’etichetta per la sezione dove scrivere
ScriveRecord (gsCategoria, sSezione, TITOLO, StringSegment (sTitoli, BELL, gnTipo + (j - 1)))
If gnTipo <= SECONDA Then; se si è nei primi due tipi di procedura,
Let sPulsanti = lstPulsantiGuidate1; imposta i pulsanti modifica, Aggiunge, Elimina, Sposta Su e Giù
Else; altrimenti, nel caso della elaborazione su più campi,
If j == PRIMA Then; se si è nel primo campo,
Let sPulsanti = lstPulsantiGuidata2; imposta quelli con avanti e indietro, e Duplica script
Let gnMaxVoci = MAX_VOCI; imposta fino ad un massimo di 99 voci
ElIf j == iCampi Then; se invece si è sull’ultimo,
Let sPulsanti = lstPulsantiGuidata4; imposta quelli con i pulsanti Avanti, Indietro e Torna all’inizio
Let gnMaxVoci = MAX_VOCI; imposta fino ad un massimo di 99 voci
Else; altrimenti, negli altri campi,
Let sPulsanti = lstPulsantiGuidata3; imposta la serie senza Aggiungi ed Elimina
Let gnMaxVoci = PRIMA; riduce ad una sola la voce di scelta visualizzata
EndIf; fine controllo campo
EndIf; fine controllo tipo
Let sPulsanti = ConverteInBell (sPulsanti, LF); converte l’elenco esteso in voci
For k = 1 To StringSegmentCount (sPulsanti, BELL); scorre i dati rilevati
Let sDato = StringSegment (sPulsanti, BELL, k); estrae la prima coppia di chiavi e dati
; utilizza la prima parte fino all’Uguale come chiave, ed il resto della stringa come suo contenuto
ScriveRecord (gsCategoria, sSezione, StringSegment (sDato, UGUALE, PRIMA), StringSegment (sDato, UGUALE, SECONDA))
EndFor; fine scansione pulsanti
ScriveNumerico (gsCategoria, sSezione, VOCI, gnMaxVoci); trascrive le voci massime selezionabili
; trascrive le impostazioni sulla presenza o meno di un prefisso numerico alle voci di scelta
ScriveNumerico (gsCategoria, sSezione, NUMERICO, gnNumerico)
If gsCategoria != SCRIPTS ; se non si stanno elaborando script a gestione personale,
|| j < iCampi Then; oppure, se comunque non ci si trova nell’ultimo campo,
; trascrive le informazioni per le fasi di aggiunta o modifica testo
ScriveRecord (gsCategoria, sSezione, INFO, StringSegment (sInfo, BELL, gnTipo))
Else; altrimenti,
ScriveRecord (gsCategoria, sSezione, INFO, NULLO); trascrive un dato vuoto
EndIf; fine controllo campo
If gsCategoria == MACRO ; se si stanno elaborando le macro,
|| (gsCategoria == SCRIPTS ; oppure, se sono da elaborare gli script a gestione personale,
&& j == QUARTA) Then; ma ci si trova solo nel quarto campo,
Let gnMultiple = TRUE; imposta l’immissione testo su più righe
Else; altrimenti,
Let gnMultiple = FALSE; la disattiva
EndIf; fine controllo righe multiple
; trascrive l’impostazione attiva per le righe multiple in fase di immissione testo
ScriveNumerico (gsCategoria, sSezione, MULTIPLE, gnMultiple)
If j >= SECONDA Then; se si è un campo dal secondo in poi,
Let gnConferma = TRUE; imposta la richiesta di una conferma all’abbandono
Else; altrimenti, se si è nella schermata iniziale,
Let gnConferma = FALSE; la disattiva
EndIf; fine controllo conferme
; trascrive l’impostazione attiva per la conferma all’uscita dalla fase attiva
ScriveNumerico (gsCategoria, sSezione, CONFERMA, gnConferma)
; trascrive anche i prefissi per le funzioni da chiamare a fine procedura
ScriveRecord (gsCategoria, sSezione, CHIAMA, StringSegment (sPrefissi, BELL, gnTipo + (j - 1)))
EndFor; fine scansione campi
Return TRUE; se nessun altro controllo ha interrotto il flusso, restituisce l’esito positivo
EndFunction
Esercizio 12.2.3. La funzione FileUtente.
FileScript.
Default.JSS
Nome.
FileUtente
Descrizione.
Determina se il file specificato esista nella cartella delle Impostazioni personali.
Ritorni.
Di tipo Int. TRUE, se il file specificato esiste; FALSE, se invece non è presente.
Parametri.
- sNome. Il nome del file da controllare. Di tipo String.
Novità.
- La funzione integrata
FileExists
, (FileEsiste); Restituisce un valore positivo se esiste un file dal nome e dal percorso indicati nel suo unico parametro.
Fasi.
- Tramite la nostra funzione
ImpostaConfigurazione ()
, si aggiunge casomai l’estensione predefinita per tali tipo di file. - Il nome così ottenuto viene formattato assieme al risultato della funzione integrata
FileExists ()
, la quale restituisce il percorso con le impostazioni per l’Utente, inserendo tra essi un carattere Controbarra.
Codice.
Int Function FileUtente (string sNome)
ImpostaConfigurazione (sNome); se non presente, aggiunge l’estensione dei file configurazione
; restituisce il risultato del controllo sull’esistenza del file specificato
Return FileExists (FormatString (msg2Controbarra, GetUserSettingsDirectory (), sNome))
EndFunction
Esercizio 12.2.4. La funzione ArchivioDati.
FileScript.
Default.JSS
Nome.
ArchivioDati
Descrizione.
Imposta in una variabile globale il nome dell’archivio dove leggere i dati, controllando l’esistenza ed il corretto funzionamento anche dell’apposito file relativo alla procedura.
Ritorni.
Di tipo Int. TRUE, nel caso di un archivio correttamente impostato; FALSE, in qualunque caso di errore.
Novità.
- Le nostre funzioni
FileUtente (),
ConfigurazioneCampi ()
e
LeggeNumerico ().
Fasi.
- Una prima struttura di controllo verifica se l’archivio di configurazione relativo alla categoria preimpostata esista, tramite la nostra funzione
FileUtente ()
; se l’archivio non c’è, viene creato grazie alla nostra
ConfigurazioneCampi ()
,e l’elaborazione continua; se però questo processo di creazione genera degli errori, il flusso viene invece interrotto. - Una seconda breve struttura controlla, e casomai imposta, il tipo di procedura elaborata, anche tramite la citata
LeggeNumerico (). - L’ultima parte della funzione si occupa di rilevare il nome dell’archivio da elaborare, sempre impostandolo in una variabile globale, prima cercando nel file configurazione, poi casomai utilizzando il nome dell’applicativo corrente.
Note.
- Questa funzione, che finalizza il lavoro delle altre presenti in questo blocco, serve a riunire in un’unica istruzione l’esigenza di controllare se tutti i dati necessari alle procedure siano disponibili, e quindi se siano stati impostati nelle variabili globali.
- Anche in questo caso, l’utilizzo delle variabili globali per rendere disponibili i dati consente di non servirsi di parametri per il suo funzionamento.
Codice.
Int Function ArchivioDati ()
If !FileUtente (gsCategoria) Then; se il file di configurazione per il tipo di procedura non esiste,
If !ConfigurazioneCampi () Then; se la creazione del file non riesce,
Return FALSE; restituisce un risultato nullo
EndIf; fine controllo creazione file configurazione
EndIf; fine controllo esistenza file configurazione
If !gnTipo Then; se il tipo di procedura non è stato ancora impostato,
Let gnTipo = LeggeNumerico (gsCategoria, ELABORA, TIPO); legge il dato dal file configurazione
EndIf; fine controllo tipo procedura
Let gsArchivio = LeggeRecord (gsCategoria, ELABORA, ARCHIVIO); legge il nome dell’archivio dei dati
If !gsArchivio Then; se nessun nome è stato rilevato,
Let gsArchivio = GetActiveConfiguration (); salva il nome dell’applicazione corrente
EndIf; fine controllo presenza settaggio
Return TRUE; restituisce l’esito positivo
EndFunction
***
I Dati registrati.
In questa corposa sezione del capitolo sono ricomprese una serie di funzioni che riconducono tutte alla ricerca, al controllo ed all’eventuale prima impostazione delle stringhe da elaborare. Il numero degli elementi di codice coinvolti è reso notevole dalla presenza di alcune funzioni chiave, sia per l’immissione materiale delle stringhe, sia per una nuova versione di quella per la richiesta di conferme, che poi saranno usate molto spesso da qui in avanti.
Proprio per questo, le funzioni trattate saranno suddivise in alcuni blocchi, che saranno illustrati tramite degli appositi titoli. Purtroppo, per realizzare un vero collaudo del codice contenuto in questa sezione, e nelle due che seguiranno, bisognerà attendere con pazienza la fine del capitolo.
Adattare messaggi e titoli delle finestre di dialogo.
Il primo blocco di questa sezione, composto da cinque funzioni, è finalizzato alla proposizione di un controllo che consente o meno di far proseguire il flusso. A caratterizzare però questo blocco è soprattutto l’aggiornamento della funzione che chiede la conferma delle scelte.
Nel dettaglio, tale modifica deve consentire alla funzione di essere chiamata senza influire sui dati già memorizzati dalla procedura chiamante . A questo compito è destinato il terzo elemento di questo blocco, che gestisce la conservazione dei dati preesistenti, sfruttando sia i parametri per riferimento, che trasmetteranno i valori validi solo a livello locale, sia le variabili globali, che serviranno per restituire i dati modificati provvisoriamente dalla funzione di conferma.
I primi due elementi del blocco, infine, sono dedicati alla coniugazione al plurale e singolare delle desinenze femminili e maschili dei termini, che servono a personalizzare i messaggi nelle finestre di dialogo.
Esercizio 12.3.2. La funzione PersonaFemminile.
FileScript.
Default.JSS
Nome.
PersonaFemminile
Descrizione.
Determina quale vocale femminile restituire, sulla base del valore immesso come parametro.
Ritorni.
Di tipo String. La vocale impostata sulla base del valore immesso.
Parametri.
- iValore. Il valore che determina la vocale da restituire, sulla base della persona singolare, ponendo il numero 1, o plurale, specificando qualsiasi altro valore. Di tipo Int.
Novità.
- La costante
_E
, che include l’omonimo carattere minuscolo.
Note.
- Questa funzione è la prima di una coppia che svolge lo stesso compito, personalizzando dei termini sulla base del valore immesso come parametro; le due opzioni previste dalla funzione sono quelle più ricorrenti, mentre le eccezioni dovranno essere trattate con modifiche più mirate.
Codice.
String Function PersonaFemminile (int iValore)
If iValore == PRIMA Then; se si deve completare un termine singolare,
Return _A; restituisce l’omonima costante
Else; altrimenti, con tutti gli altri valori, che indicano un termine plurale,
Return _E; restituisce l’omonima costante
EndIf; fine controllo valore
EndFunction
Esercizio 12.3.3. La funzione PersonaMaschile.
FileScript.
Default.JSS
Nome.
PersonaMaschile
Descrizione.
Determina quale vocale maschile restituire, sulla base del valore immesso come parametro.
Ritorni.
Di tipo String. La vocale impostata sulla base del valore immesso.
Parametri.
- iValore. Il numero che determina la vocale da restituire: 1, per un termine singolare, o tutti gli altri, per un termine plurale. Di tipo Int.
Novità.
- La costante
_I
, che equivale all’omonimo carattere minuscolo.
Codice.
String Function PersonaMaschile (int iValore)
If iValore == PRIMA Then; se si deve completare un termine singolare,
Return _O; restituisce l’omonima costante
Else; altrimenti, con tutti gli altri valori, che indicano un termine plurale,
Return _I; restituisce l’omonima costante
EndIf; fine controllo valore
EndFunction
Esercizio 12.3.4. La funzione ScambiaTitoli.
FileScript.
Default.JSS
Nome.
ScambiaTitoli
Descrizione.
Se si indica un primo parametro, esso viene registrato in una variabile globale riservata al titolo, salvando l’eventuale precedente contenuto in una variabile locale trasmessa per riferimento; inoltre, sarà azzerato il dato di un’altra variabile globale dedicata alle etichette dei pulsanti nelle finestre di dialogo, anche qui salvandone casomai il contenuto in una variabile locale trasmessa per riferimento. Nel caso in cui non si indichi un primo parametro, i valori delle variabili locali sono riassegnati alle rispettive variabili globali.
Ritorni.
Di tipo Void. Nessuno.
Parametri.
- sTitolo. Il testo da registrare come titolo. Di tipo String.
- sBufferTitolo. Per Riferimento. L’eventuale precedente contenuto della variabile globale con il titolo della finestra di dialogo. Di tipo String.
- sBufferEtichette. Per Riferimento. L’eventuale precedente contenuto della variabile globale con le etichette dei pulsanti della finestra di dialogo. Di tipo String.
Note.
- Il motivo che costringe a cambiare il contenuto di alcune variabili globali è che tale contenuto è utilizzato da altri script, in particolare da quello che legge il titolo della finestra e dal nostro che propone l’Aiuto in linea, per pronunciare o porre nel Visualizzatore Virtuale alcune informazioni. Poiché tali informazioni cambiano se i citati script sono richiamati dalla finestra principale, oppure da una finestra di dialogo secondaria, è necessario che, alla comparsa di una nuova finestra, il contenuto delle variabili globali vada di volta in volta aggiornato.
- Questa funzione ricorda per certi versi il sistema di memorizzazione usato per lo script
AiutoInLinea ()
, come indica il prefisso
"buffer"
inserito prima del nome effettivo della variabile locale.
Codice.
Void Function ScambiaTitoli (string sTitolo, ; intestazione con il primo parametro,
string ByRef sBufferTitolo, string ByRef sBufferEtichette); seconda riga con gli altri due
If sTitolo Then; se il primo parametro è stato indicato,
Let sBufferTitolo = gsTitolo; salva l’eventuale titolo già registrato,
Let gsTitolo = sTitolo; e assegna alla variabile globale il parametro indicato
Let sBufferEtichette = gsEtichette ; memorizza le eventuali etichette impostate,
Let gsEtichette = NULLO; e azzera la variabile globale, per impedire la lettura dell’Aiuto in linea
Else; altrimenti, se il primo parametro non è stato indicato,
Let gsTitolo = sBufferTitolo; riassegna il titolo precedentemente salvato
Let gsEtichette = sBufferEtichette; riassegna anche le etichette casomai registrate
EndIf; fine controllo titolo
EndFunction
Esercizio 12.3.5. La versione aggiornata di ChiedeConferma ().
FileScript.
Default.JSS
Nome.
ChiedeConferma
Novità.
- La nostra funzione
ScambiaTitoli ().
Note.
- Le prime modifiche alla precedente versione, di cui si è accennato ad inizio sezione, sono le dichiarazioni delle due variabili locali, usate per memorizzare i dati attivi, poste tra quelle delle variabili
"iStato"
e
"iDettagli"
, nella seguente forma: - La seconda modifica riguarda la prima chiamata della nostra funzione appena realizzata, che memorizza i dati attivi, la quale va posta subito dopo la prima struttura di controllo nella seguente forma su due righe:
- La terza ed ultima modifica riguarda il ripristino dei dati salvati nelle variabili locali, memorizzandoli nelle corrispettive variabili globali, ponendo appena sotto la seconda chiamata della nostra funzione
ControllaSintesi ()
, la seguente istruzione: - Come al solito, in caso di dubbi, fate riferimento alla forma modificata, che poniamo di seguito.
String sBufferTitolo, ; eventuale precedente contenuto del titolo predefinito
String sBufferEtichette, ; eventuale precedente contenuto delle etichette dei pulsanti
; memorizza gli eventuali contenuti di titolo ed etichette della finestra di dialogo
ScambiaTitoli (FormatString (msg2Spazi, sTitolo, sMessaggio), sBufferTitolo, sBufferEtichette)
ScambiaTitoli (NULLO, sBufferTitolo, sBufferEtichette); ripristina le variabili globali
Codice.
Int Function ChiedeConferma (string sTitolo, string sMessaggio, ; intestazione con due parametri,
int iOpzioni, int iTasto, int iNoSuoni); seconda riga con gli altri tre
Var
Int iStato, ; attivazione della sintesi vocale
String sBufferTitolo, ; eventuale precedente contenuto del titolo predefinito
String sBufferEtichette, ; eventuale precedente contenuto delle etichette dei pulsanti
int iDettagli, ; valore esadecimale per icona e suoni
Int iScelta; valore della scelta effettuata
Let iStato = ControllaSintesi (TRUE, TRUE); riattiva la sintesi, annotandone lo stato d’attivazione
If !sTitolo ; se nessun titolo è stato specificato,
&& !sMessaggio Then; e se non lo è stato neppure un messaggio,
Let sTitolo = msgConfermaUscita; imposta un messaggio come titolo
EndIf; fine controllo titolo e messaggio
; memorizza gli eventuali contenuti di titolo ed etichette della finestra di dialogo
ScambiaTitoli (FormatString (msg2Spazi, sTitolo, sMessaggio), sBufferTitolo, sBufferEtichette)
AssegnaEsadecimali (iOpzioni, iDettagli); imposta i valori tramite l’apposita funzione
If !iTasto || iTasto == PRIMA Then; se non è stato indicato nulla, oppure il primo valore,
Let iTasto = MB_DEFBUTTON1; aggiorna come predefinito il primo pulsante nella linea dei tasti Tab
ElIf iTasto == SECONDA Then; se invece è stato indicato il secondo valore,
Let iTasto = MB_DEFBUTTON2; aggiorna come predefinito il secondo pulsante
ElIf iTasto == TERZA Then; se invece è stato indicato il terzo valore,
Let iTasto = MB_DEFBUTTON3; aggiorna come predefinito il terzo pulsante
Else; altrimenti,
Let iTasto = MB_DEFBUTTON4; aggiorna come predefinito il quarto pulsante
EndIf; fine controllo tasto predefinito
If !iNoSuoni Then; se non è stata indicata la cancellazione dei suoni,
Let iScelta = ExMessageBox (sMessaggio, sTitolo, iOpzioni|iTasto|iDettagli); propone la classica conferma
Else; altrimenti,
Let iScelta = ExMessageBox (sMessaggio, sTitolo, iOpzioni|iTasto); la propone senza icona e suoni
EndIf; fine controllo sintassi
ControllaSintesi (iStato, TRUE); ripristina, o lascia invariato, il precedente stato di attivazione della sintesi
ScambiaTitoli (NULLO, sBufferTitolo, sBufferEtichette); ripristina le variabili globali
If iScelta == IDNO Then; se si è premuto il tasto No,
Return -1; restituisce un risultato negativo
ElIf iScelta == IDCANCEL Then; se invece si è premuto Escape o Annulla,
Return FALSE; restituisce un risultato nullo
Else; altrimenti,
Return iScelta; restituisce la scelta operata
EndIf; fine controllo scelta
EndFunction
Esercizio 12.3.6. La funzione ProsegueFlusso.
FileScript.
Default.JSS
Nome.
ProsegueFlusso
Descrizione.
Consente di proseguire o interrompere la creazione di nuovi elementi durante l’utilizzo di una procedura.
Ritorni.
Di tipo Int. TRUE per confermare la prosecuzione, FALSE per interromperla.
Parametri.
- sCategoria. Il tipo di dato da elaborare. Di tipo String.
- sSuffisso. Ultimo termine dello script chiamante. Di tipo String.
Novità.
- Le nostre funzioni
PersonaFemminile ()
e
PersonaMaschile ().
Fasi.
- La prima struttura di controllo imposta la desinenza dei termini nel titolo e nel messaggio della finestra di dialogo, grazie alla nostra funzione
PersonaFemminile ()
e sulla base della categoria della procedura attiva. - Una seconda struttura imposta invece un suffisso per il messaggio, solo nel caso che il tipo di procedura sia il terzo.
- Dopo aver impostato il titolo, un’ultima struttura controlla ancora l’impostazione di una desinenza, tramite la citata funzione
PersonaMaschile (). - Viene infine restituito il risultato della funzione con la richiesta di conferma, il cui messaggio viene direttamente formattato come parametro della funzione stessa.
Codice.
Int Function ProsegueFlusso (string sCategoria, string sSuffisso)
Var
String sFinale, ; lettera conclusiva di un termine
String sTermine, ; oggetto da elaborare
String sDato, ; tipologia di suffisso
String sTitolo; titolo della finestra di dialogo
If sCategoria == MACRO Then; se si elaborano delle macro,
Let sFinale = PersonaFemminile (PRIMA); imposta la desinenza singolare femminile
ElIf sCategoria == SCRIPTS Then; se invece si elaborano gli script a gestione personale,
Let sFinale = PersonaMaschile (PRIMA); imposta la desinenza singolare maschile
EndIf; fine controllo categoria
If gnTipo == TERZA Then; se si stanno elaborando procedure su più campi,
Let sTermine = sCategoria; imposta come oggetto il solo nome della categoria,
; imposta come suffisso il nome dell’applicazione corrente
Let sSuffisso = GetActiveConfiguration ()
Else; altrimenti,
Let sTermine = sCategoria + gsOggetto; compone categoria e oggetto globale
EndIf; fine controllo tipo procedura
Let sDato = StringSegment (lstTipiDato, PIPE, gnTipo); estrae il tipo di suffisso,
Let sTitolo = FormatString (ttlNoElementi, sFinale, sTermine, sDato, sSuffisso); e crea il titolo
If sCategoria == ELEMENTO Then; se si stanno elaborando gli elementi di ricerca,
Let sFinale = PersonaMaschile (PRIMA); la mette al maschile
EndIf; fine controllo prefisso
Return ChiedeConferma (sTitolo, FormatString (msgImmissione, sFinale)); restituisce la scelta
EndFunction
Due elementi autonomi.
Le prossime due funzioni sono due elementi a sé stanti, che hanno in comune solo il largo uso delle variabili globali.
La prima delle due serve solo a determinare quale termine sarà formattato nel messaggio delle finestre di dialogo, sulla base del tipo di procedura attiva.
La seconda si occupa invece di creare degli elenchi di voci, suddivise tramite i caratteri indicati o un separatore predefinito. Tali elenchi possono essere di tutte le voci in memoria, parziali o composti di un’unica voce, sulla base dei parametri indicati e dei dati impostati per la procedura attiva.
Esercizio 12.3.8. La funzione ComponeTermine.
FileScript.
Default.JSS
Nome.
ComponeTermine
Descrizione.
Controlla quale termine da formattare andrà restituito, sulla base del tipo di procedura attiva.
Ritorni.
Di tipo String. Il termine da formattare nelle finestre di dialogo.
Novità.
- La costante
UNO
, che include il valore dell’omonimo numero.
Note.
- I dati restituiti, sempre sulla base del tipo di procedura attiva, possono provenire entrambi da variabili globali, il cui significato preciso sarà illustrato solo più avanti.
- Nel caso della prima via della struttura di controllo, qualora la variabile globale fosse vuota, sarebbe restituito il valore testuale di 1, tramite la citata costante.
Codice.
String Function ComponeTermine ()
If gnTipo <= SECONDA Then; se ci si trova nei primi due tipi di procedura,
Return gsChiave; restituisce il valore testuale della chiave come termine da formattare
Else; altrimenti, nelle procedure del terzo tipo,
Return gsAttiva; restituisce la voce corrente del campo principale
EndIf; fine controllo termine da formattare
EndFunction
Esercizio 12.3.9. La funzione ChiaveCorrente.
FileScript.
Default.JSS
Nome.
ChiaveCorrente
Descrizione.
Restituisce la forma testuale della chiave attiva, sulla base del tipo di procedura che si sta elaborando.
Ritorni.
Di tipo String. La chiave attiva in forma testuale.
Parametri.
- iValore. Il valore numerico riferito alla chiave corrente. Di tipo Int.
Note.
- La funzione contiene un’unica struttura di controllo che, sulla base del tipo di procedura che si sta elaborando, sceglie di restituire o la forma testuale del valore passato come parametro, oppure la chiave nella posizione del valore specificato all’interno dell’elenco globale impostato.
Codice.
String Function ChiaveCorrente (int iValore)
If gnTipo <= SECONDA Then; se si stanno elaborando i primi due tipi di procedura,
Return IntToString (iValore); restituisce la forma testuale del valore corrente
Else; altrimenti, se si è nel terzo tipo di procedura,
Return StringSegment (gsElenco, PIPE, iValore); restituisce la chiave attiva nell’elenco
EndIf; fine controllo tipo procedura
EndFunction
Esercizio 12.3.10. La funzione CreaElenco.
FileScript.
Default.JSS
Nome.
CreaElenco
Descrizione.
Crea un elenco di elementi da utilizzare in ricerche e confronti.
Ritorni.
Di tipo String. L’elenco degli elementi da elaborare.
Parametri.
- sArchivio. Il nome, senza estensione, dell’archivio da elaborare. Di tipo String.
- sSezione. La sezione dell’archivio in cui operare. Di tipo String.
- iCampo. L’eventuale numero del campo di cui estrarre i dati; se non specificato, viene usato quello generato dalla nostra apposita funzione. Di tipo Int. Parametro Opzionale.
- sSeparatore. L’eventuale separatore degli elementi nell’elenco; se non specificato, viene usato il carattere Bell, Asci 7. Di tipo String. Parametro Opzionale.
Fasi.
- Le prime due strutture controllano che un dato sia stato specificato come terzo o quarto parametro, il numero di campo o il carattere separatore, altrimenti ne assegnano il valore predefinito.
- Se ci si trova in uno dei primi due tipi di procedura, si tenta di leggere la posizione dell’ultimo dato elaborato, per considerarla come numero massimo di voci utilizzabili tra quelle esistenti.
- Se nessun precedente dato è registrato, o ci si trova in una procedura a più campi, il numero delle voci da utilizzare coincide con quello totale delle voci presenti nell’elenco.
- Un ciclo scorre le voci registrate, aggiungendo tra di esse solo quelle necessarie e restituendo poi la nuova forma dell’elenco, nella quale è stato casomai sostituito il carattere di separazione tra le voci.
Note.
- In pratica, la funzione serve soprattutto quando, tra gli elementi di ricerca registrati per l’estensione del documento corrente, ne siano registrati ad esempio tutti i nove possibili, ma si vuole limitare la ricerca solo ai primi tra essi.
Codice.
String Function CreaElenco (string sArchivio, string sSezione, ; primi due parametri,
int iCampo, string sSeparatore); seconda riga con gli altri due
Var
Int iValide, ; numero delle voci da inserire nell’elenco
Int i, ; contatore del ciclo
String sRecord, ; dati registrati
String sChiave, ; chiave del record
String sVoci; elenco da restituire
If !iCampo Then; se nessun numero di campo è stato specificato,
Let iCampo = CampoGlobale (); viene usato il valore nella variabile globale, o il numero 1
EndIf; fine controllo campo
If !sSeparatore Then; se nessun separatore delle voci di elenco è stato specificato,
Let sSeparatore = BELL; viene usato il carattere Bell, Ascii 7
EndIf; fine controllo separatore
If gnTipo <= SECONDA Then; se si è nei primi due tipi di elaborazione,
Let iValide = StringToInt (LeggeRecord (sArchivio, sSezione, ZERO)); legge il dato sull’ultima impostazione
If !iValide ; se il valore non è stato rilevato,
&& gnTotali >= CICLO_VOCI Then; e le voci sono maggiori o uguali al valore predefinito,
Let iValide = CICLO_VOCI; lo imposta
EndIf; fine primo controllo voci
EndIf; fine controllo tipo elaborazione
If !iValide Then; se il valore non è ancora stato definito,
Let iValide = gnTotali; imposta il numero totale delle voci registrate
EndIf; fine controllo impostazione valore
For i = 1 To iValide; scorre il numero di voci impostato
Let sChiave = ChiaveCorrente (i); ricava la chiave attiva dall’apposita funzione
Let sRecord = LeggeRecord (sArchivio, sSezione, sChiave); rileva l’intero record di dati
; aggiunge all’elenco delle voci da comporre il dato riferito al campo attivo
Let sVoci = CambiaCampo (sVoci, StringSegment (sRecord, PIPE, iCampo), ULTIMA, sSeparatore)
EndFor; fine scansione chiavi
Return sVoci; restituisce il nuovo elenco con i dati
EndFunction
Collaudo.
- Dopo aver correttamente compilato, è necessario porre come opzionali gli ultimi due parametri.
Immissione e modifica di un testo.
La prossima coppia di funzioni si occupa dell’immissione materiale del testo nelle nostre procedure. Per quanto riguarda il testo da trattare, va chiarito che noi avremo due formati principali per farlo:
-
riga unica
, nella quale gli eventuali contenuti di più righe sono posti l’uno accanto all’altro, come i campi di un record, e divisi tra loro da una speciale stringa di separazione composta da una coppia di parentesi quadre con dentro due punti esclamativi. -
su più righe
, dove gli eventuali diversi contenuti sono suddivisi dai normali caratteri di fine riga, Ritorno a Capo e Nuova Linea, Ascii 13 e 10.
Nel dettaglio, il primo formato è quello che ci consente di registrare, e poi leggere, i nostri dati negli archivi in formato
"INI"
, mentre il secondo è quello che ci consente di poter modificare tali dati tramite lo strumento per inserire testo, la funzione
InputBox ().
Tornando agli elementi da realizzare, il primo di questi sarà una funzione che si occupa di convertire il testo elaborato nei due possibili formati, mentre il secondo contiene la procedura di immissione vera e propria, quella che richiama la citata funzione nativa.
In particolare, questa seconda funzione contiene anche un ciclo che analizza il testo inserito, consentendo di ritornare all’inserimento nel caso in cui:
- non si sia specificato alcun testo, oppure si sia usciti dall’immissione.
- Il testo inserito sia compreso nell’elenco di quelli vietati, specificato in un parametro della funzione stessa.
- Nel testo sia presente il Carattere Pipe, e tra i parametri della funzione si è indicato che ciò non sia possibile.
Inoltre, l’ingresso e l’uscita dal campo d’inserimento sono segnalati dai suoni che Jaws propone, per le stesse fasi, ad esempio nei campi d’immissione delle pagine web, per dare anche un riscontro sonoro sulle azioni da svolgere.
Esercizio 12.3.12. La funzione InverteForma.
FileScript.
Default.JSS
Nome.
InverteForma
Descrizione.
Converte i caratteri di fine riga nei separatori di voce, lasciando vuoto il secondo parametro o ponendovi il valore FALSE, oppure, viceversa, dai separatori di voce ai caratteri di fine riga, ponendo TRUE nel secondo parametro. Come sequenza di fine riga, sarà utilizzata quella classica, Ascii 13 e 10, nel caso in cui come terzo parametro non sia indicato nulla, altrimenti si utilizzerà quella specificata.
Ritorni.
Di tipo String. Il testo di cui si siano eventualmente convertiti i caratteri di fine riga o i separatori di voce, sulla base del tipo eventualmente specificato come secondo parametro.
Parametri.
- sTesto. La stringa testuale da convertire. Di tipo String.
- iTipo. Se valorizzato con TRUE, converte gli eventuali separatori di voce, definiti tramite l’apposita costante personale, nei caratteri di fine riga, predefiniti o impostati nel terzo parametro; lasciando vuoto o mettendo il valore FALSE, viceversa, i caratteri di fine riga saranno convertiti nei separatori di voce. Di tipo Int. Parametro Opzionale.
- sRitorno. L’eventuale carattere di ritorno a capo per la sostituzione; qualora non si specifichi nulla, sarà usata la classica sequenza di ritorno a capo, Ascii 13 e 10. Di tipo String. Parametro Opzionale.
Novità.
- La costante
RN
, che equivale alla classica sequenza di ritorno a capo composta dai caratteri Ascii 13 e 10, e la costante
SPV
, che contiene il separatore di voce personale, costituito da due punti esclamativi dentro ad una coppia di parentesi quadre.
Note.
- L’utilizzo più semplice della funzione, specificando solo il testo da convertire, sostituisce gli eventuali caratteri di Ritorno a Capo nei Separatori di Voce.
- Se oltre al testo si pone anche la costante
TRUE
nel secondo parametro, saranno i Separatori di Voce ad essere convertiti nei caratteri definiti come ritorni a capo. - Se poi si specifica un terzo parametro, i caratteri in esso indicati saranno considerati come i ritorni a capo per le sostituzioni.
Codice.
String Function InverteForma (string sTesto, int iTipo, string sRitorno)
If !sRitorno Then; se non è stata specificata alcuna stringa di fine riga alternativa,
Let sRitorno = RN; imposta come fine riga la sequenza classica, Ascii 13 e 10
EndIf; fine controllo caratteri di fine riga
If iTipo Then; se si è richiesta la conversione dai separatori di voce ai caratteri di fine riga,
Return StringReplaceSubstrings (sTesto, SPV,sRitorno); sostituisce i primi con i secondi
Else; altrimenti,
Return StringReplaceSubstrings (sTesto, sRitorno, SPV); cambia i fine riga con i separatori di voce
EndIf; fine controllo tipo conversione
EndFunction
Collaudo.
- Anche in questo caso, dopo aver compilato la funzione, aprite il file documentazione per rendere opzionali il secondo e terzo parametro.
Esercizio 12.3.13. La funzione InserisceTesto.
FileScript.
Default.JSS
Nome.
InserisceTesto
Descrizione.
Consente di inserire del testo in un normale campo d’immissione stile Windows; nel caso non si specifichi nulla, o si abbandoni l’inserimento, viene proposta una ulteriore conferma se uscire o se tornare all’immissione di testo. Se si specifica un elenco di stringhe testuali come quarto parametro, si controlla che il testo immesso non sia già presente nell’elenco, casomai costringendo a modificarlo. Se non si specifica nulla come quinto parametro, qualora si immetta anche un carattere Pipe, Ascii 124, viene segnalata la necessità di rimuoverlo.
Ritorni.
Di tipo String. Il testo eventualmente inserito.
Parametri.
- sTitolo. L’eventuale titolo da proporre nella finestra d’inserimento; qualora non lo si specifichi, viene assunto un titolo predefinito. Di tipo String. Parametro Opzionale.
- sTesto. L’eventuale stringa che sarà predisposta nel campo d’inserimento; qualora non la si specifichi, il campo sarà vuoto. Di tipo String. Parametro Opzionale.
- iMultiple. Se valorizzato con TRUE, indica di considerare il testo su righe multiple, quindi riconvertendolo all’uscita con i separatori di voce lineari; nel caso non si indichi nulla, il testo verrà troncato al primo eventuale carattere di ritorno a capo. Di tipo Int. Parametro Opzionale.
- sVietati. L’eventuale elenco di termini, separati dal carattere Pipe, che non devono essere immessi, pena l’interruzione dell’inserimento e l’invito alla modifica. Di tipo String. Parametro Opzionale.
- iPipe. Se valorizzato con TRUE, consente di immettere nel campo testuale anche il carattere Pipe, Ascii 124; se non si specifica nulla, tale inserimento causerà un messaggio d’errore. Di tipo Int. Parametro Opzionale.
Novità.
- La nostra funzione
InverteForma (). - Le costanti
INGRESSO_CAMPO
e
USCITA_CAMPO
, che equivalgono ai file di suoni, senza estensione, impostati nel file delle costanti personali
_PSConst.JSH.
Fasi.
- Le impostazioni iniziali prevedono comunque di attivare la sintesi vocale, e sostituire poi gli eventuali separatori di voce in ritorni a capo, grazie alla nostra funzione
InverteForma ()
, quindi di determinare il testo del messaggio a video, ed infine di salvare eventuali dati memorizzati circa il titolo e le etichette dei pulsanti della funzione chiamante. - Si entra poi nel ciclo d’inserimento testo dove, prima e dopo la chiamata alla funzione integrata d’immissione testo, viene emesso un suono.
- Il testo eventualmente inserito viene sottoposto ai tre tipi di controlli, elencati nella premessa, che possono far ritornare all’inserimento; se il testo immesso è valido, viene casomai riconvertito nel formato lineare, o interrotto al primo carattere di ritorno a capo, ed in ogni caso il flusso viene fermato restituendo l’esito positivo.
- Se invece si conferma l’abbandono dell’immissione, si esce dal ciclo.
- In ogni caso, sono ripristinati lo stato di Attivazione della sintesi, nonché i dati memorizzati su titoli ed etichette, al momento della chiamata della funzione.
Codice.
String Function InserisceTesto (string sTitolo, string sTesto, ; primi due parametri,
int iMultiple, string sVietati, int iPipe); seconda riga con gli altri tre
Var
Int iStato, ; stato di attivazione della sintesi
String sPrefisso, ; testo da premettere al messaggio
String sMessaggio, ; testo informativo
String sBufferTitolo, ; eventuale precedente contenuto del titolo predefinito
String sBufferEtichette, ; eventuale precedente contenuto delle etichette dei pulsanti
Int iScelta, ; l’esito delle finestre di dialogo
String sDato; variabile temporanea
Let iStato = ControllaSintesi (TRUE, TRUE); riattiva la sintesi, annotandone il precedente stato
; cambia la forma della stringa da estesa a quella con ritorni a capo
Let sTesto = InverteForma (sTesto, TRUE)
If iMultiple Then; se il testo può essere scritto su più righe,
Let sPrefisso = msgRigheMultiple; imposta il prefisso da formattare nel messaggio
EndIf; fine controllo righe immissione
Let sMessaggio = FormatString (msgMettiTesto, sPrefisso); formatta il testo del messaggio
; memorizza i contenuti del titolo, e delle etichette dei pulsanti, della finestra di dialogo
ScambiaTitoli (FormatString (msg2Spazi, sTitolo, sMessaggio), sBufferTitolo, sBufferEtichette)
While iScelta <= FALSE ; continua finché il valore di controllo rimane non superiore a 0
Let sDato = sTesto; duplica la stringa, per non compromettere l’originale
Suona (INGRESSO_CAMPO); emette un suono di avvio editazione
Let iScelta = InputBox (sMessaggio, sTitolo, sDato); consente l’immissione o la modifica di una stringa
Suona (USCITA_CAMPO); emette un suono di uscita dall’editazione
If !sDato Then; se la stringa è vuota, anche per l’abbandono dell’inserimento,
Let iScelta = ChiedeConferma (ttlNoModifica, msgConferma, FALSE, SECONDA); propone la conferma
ElIf StringSegmentIndex (sVietati, BELL, StringLower (sDato), TRUE) Then; se il dato immesso è già presente,
ChiedeConferma (FormatString (ttlPresenzaDato, gsTermine, sDato), msgAggiornaTesto, OK)
Let iScelta = FALSE; imposta il valore per il ritorno nel campo d’immissione
ElIf !iPipe && StringContains (sDato, PIPE) Then; se è stato inserito anche il carattere Pipe,
ChiedeConferma (ttlNoPipe, msgAggiornaTesto, OK)
Let iScelta = FALSE; imposta il valore per il ritorno nel campo d’immissione
Else; altrimenti, se un testo è stato immesso,
ControllaSintesi (iStato, TRUE); ripristina lo stato della sintesi all’ingresso nell’inserimento
ScambiaTitoli (NULLO, sBufferTitolo, sBufferEtichette); ripristina le variabili globali
If !iMultiple Then; se il dato è disposto su una riga,
Return TroncaTesto (sDato, CR); interrompe la stringa sino ad un eventuale primo ritorno a capo, erroneamente presente
Else; altrimenti, restituisce la stringa con gli eventuali ritorni a capo convertiti in separatori
Return InverteForma (sDato)
EndIf; fine controllo righe multiple
EndIf; fine controllo presenza stringa
EndWhile; fine ciclo immissione
ControllaSintesi (iStato, TRUE); ripristina lo stato della sintesi all’ingresso nell’inserimento
ScambiaTitoli (NULLO, sBufferTitolo, sBufferEtichette); ripristina le variabili globali,
Return NULLO; e restituisce una stringa vuota
EndFunction
Collaudo.
- Dopo aver correttamente compilato il codice, sono da rendere opzionali tutti i parametri della funzione.
Il controllo sui tasti validi.
La prossima funzione è davvero quasi un’intrusa, tanto avanti sarà il suo primo utilizzo . Come detto più volte, tuttavia, dobbiamo sin d’ora predisporre la forma definitiva del codice, e per questo la prevediamo già come opzione delle fasi d’inserimento testo.
Il suo compito è quello di controllare che il testo passato come parametro equivalga ad un nome di un tasto, o di una combinazione di tasti, nella forma estesa in inglese. per fare ciò, si sfrutterà la funzione integrata
IsScriptKey
, (SonoTastiDiScript), che restituisce un valore positivo se, nel suo unico parametro, si indicano tasti, o la combinazione di tasti, che possono attivare uno script.
Ad esempio, il tasto
"Home"
attiva lo script
"JAWSHome"
, mentre il tasto
"End"
esegue lo script
"JAWSEnd"
. Quindi, sottoponendo alla funzione
IsScriptKey ()
i termini
"Home"
, oppure
"End"
, la funzione restituirà un esito positivo.
sarà inoltre predisposto un apposito elenco, per consentire di far superare il controllo anche ad altri termini d’uso comune non coinvolti da script. Tale elenco, generato creando gli archivi di configurazione delle procedure, potrà comunque essere aggiornato direttamente tramite una finestra di dialogo della nostra funzione, confermando la validità di eventuali nuovi termini non riconosciuti.
Esercizio 12.3.15. La funzione ConfermaAzioni.
FileScript.
Default.JSS
Nome.
ConfermaAzioni
Descrizione.
Controlla se le azioni immesse corrispondano a dei tasti riconosciuti come validi, casomai consentendo di aggiungerli all’archivio di quelli memorizzati come tali sul file di configurazione personale.
Ritorni.
Di tipo Int. TRUE, se le azioni immesse sono ritenute dei tasti validi; FALSE, se non lo sono, ed alla successiva conferma si clicca su Annulla.
Parametri.
- sTesto. Di tipo String.
Novità.
- La funzione integrata
IsScriptKey ()
, già descritta nella premessa. - La costante
VIRGOLA_SPAZIO
, che equivale alla stringa composta dai due caratteri che ne costituiscono il nome.
Fasi.
- Come impostazioni iniziali, sono dapprima raccolti i termini validi, registrati nell’apposita sezione dell’archivio di configurazione personale, suddividendo il testo specificato come parametro nelle eventuali diverse righe su cui è posto.
- Un primo ciclo analizza ciascuna riga in cui è casomai diviso il testo, controllando se si tratti di un termine valido. Se così non è, si apre un secondo ciclo nel quale si prova a considerare il termine come una combinazione tasti, sottoponendolo alla funzione integrata
IsScriptKey ()
, quindi tentando di sezionarla nelle eventuali parti separate dal segno Più. - Se anche questo secondo ciclo non riconosce il termine, lo stesso viene memorizzato come errore, aggiungendolo ad un altro elenco a ciò dedicato.
- Alla conclusione dei cicli, se i termini indicati sono tutti validi, viene restituito l’esito positivo del controllo. Se invece vi sono degli errori, i termini non riconosciuti sono proposti a video, chiedendo conferma se siano comunque validi. Se così è, i singoli termini sono aggiunti all’apposito elenco, restituendo l’esito positivo, altrimenti viene restituito un risultato nullo.
Codice.
Int Function ConfermaAzioni (string sTesto)
Var
String sValidi, ; elenco dei tasti archiviati come validi
String sDati, ; elenco dei dati immessi
Int j, ; contatore del ciclo principale
String sDato, ; singolo dato da controllare
Int k, ; contatore del secondo ciclo
String s, ; singolo frammento tra i segni di congiunzione
String sErrori, ; elenco degli errori riscontrati
String sTitolo, ; testo per la finestra di dialogo
Int x; contatore dell’ultimo ciclo
; rileva i tasti presenti nell’archivio dei validi, portandoli in caratteri minuscoli
Let sValidi = StringLower (LeggeChiavi (PERSONALE, ARCHIVIO + TASTI))
; mette dei caratteri di Nuova Linea, Ascii 10, al posto di eventuali separatori di voce
Let sDati = InverteForma (sTesto, TRUE, LF)
For j = 1 To StringSegmentCount (sDati, LF); scorre i dati passati tramite parametro
Let sDato = StringSegment (sDati, LF, j); estrae il singolo dato
If StringLength (sDato) > 1 Then; se il dato estratto è più lungo di un carattere,
If !IsScriptKey (sDato) Then; se non è riconosciuto come un tasto utilizzato da qualche script,
; scorre i frammenti eventualmente divisi dal segno di congiunzione
For k = 1 To StringSegmentCount (sDato, SEGNO_PIU)
Let s = StringSegment (sDato, SEGNO_PIU, k); estrae il singolo frammento tra segni Più
If StringLength (s) > 1 Then; se il frammento è più lungo di un carattere,
If !StringSegmentIndex (sValidi, PIPE, StringLower (s), TRUE) Then; infine, se il frammento non è tra i tasti validi,
; aggiorna l’elenco dei tasti o frammenti non riconosciuti
Let sErrori = CambiaCampo (sErrori, s, ULTIMA, BELL)
EndIf; fine controllo tasti validi
EndIf; fine controllo lunghezza frammento
EndFor; fine scansione frammenti
EndIf; fine controllo tasti script
EndIf; fine controllo lunghezza dato
EndFor; fine scansione dati
If !sErrori Then; se i tasti sono stati tutti riconosciuti,
Return TRUE; restituisce l’esito positivo
Else; altrimenti, se vi sono dei tasti non riconosciuti,
; formatta il titolo, inserendo l’elenco degli errori riscontrati
Let sTitolo = FormatString (ttlArchiviaTasti, StringReplaceSubstrings (sErrori, PIPE, VIRGOLA_SPAZIO))
If ChiedeConferma (sTitolo, msgOkDati, FALSE, SECONDA) Then; se si confermano i dati immessi,
For x = 1 To StringSegmentCount (sErrori, BELL); scorre gli errori rilevati,
; e aggiorna l’archivio dei termini validi
ScriveNumerico (PS_FILE, ARCHIVIO + TASTI, StringSegment (sErrori, BELL, x), TRUE)
EndFor; fine scansione errori
Return TRUE; restituisce l’esito positivo
Else; altrimenti, se si annulla l’aggiornamento,
Return FALSE; restituisce un risultato nullo
EndIf; fine controllo conferma
EndIf; fine controllo presenza errori
EndFunction
Le stringhe registrate.
Le restanti quattro funzioni della sezione si occupano di controllare che vi siano delle stringhe registrate per la procedura richiamata. Tra queste, da rilevare quella che scrive sugli archivi modificando il singolo campo di un record, e soprattutto quella che si occupa in generale dell’aggiornare il contenuto dei campi.
Quest’ultima, la più lunga del blocco con oltre cinquanta righe di codice, chiama a sua volta gran parte delle funzioni analizzate in questa sezione, oltre ad avere una serie di controlli al suo interno. L’elemento di codice più importante è tuttavia l’ultimo della sezione, che verifica la presenza dei dati necessari alle varie procedure, e che chiama anche la funzione di aggiornamento campi.
Esercizio 12.3.17. La funzione ChiaveGlobale.
FileScript.
Default.JSS
Nome.
ChiaveGlobale
Descrizione.
Determina la chiave testuale, duplicando il dato registrato nella omonima variabile globale oppure, se questa è vuota, prendendo la prima dell’elenco memorizzato nella variabile globale riservata a questo tipo di dato.
Ritorni.
Di tipo String. La chiave registrata, o la prima dell’elenco delle chiavi attive.
Note.
- Questa funzione serve solo per garantire che sia restituito un qualche valore, a prescindere da dove essa lo rilevi, grazie all’unica struttura di controllo che la compone. L’assenza di parametri sta ad indicare, anche in questo caso, l’utilizzo delle variabili globali per elaborare i dati.
Codice.
String Function ChiaveGlobale ()
If gsChiave Then; se un dato è registrato,
Return gsChiave; lo restituisce
ElIf gsElenco Then; se un elenco di chiavi è impostato,
Return StringSegment (gsElenco, PIPE, PRIMA); restituisce la prima chiave di tale elenco,
Else; altrimenti, se il dato è ancora da impostare,
Return UNO; restituisce il primo valore in forma testuale
EndIf; fine controllo chiave
EndFunction
Esercizio 12.3.18. La funzione ScriveCampo.
FileScript.
Default.JSS
Nome.
ScriveCampo
Descrizione.
Scrive il dato passato come parametro nel campo indicato del record specificato.
Ritorni.
Di tipo Int. l’esito della scrittura, TRUE o FALSE.
Parametri.
- sArchivio. Il nome, senza estensione, dell’archivio da elaborare. Di tipo String.
- sSezione. La sezione dell’archivio in cui operare. Di tipo String.
- sChiave. La chiave del record. Di tipo String.
- sDato. Il dato testuale da inserire nel campo indicato. Di tipo String.
Fasi.
- Come impostazioni iniziali, sono letti il record di dati e il numero del campo in cui scrivere.
- Una prima struttura di controllo verifica se il campo nel record già esiste; se così non è, e si sta elaborando una procedura su più campi, si annota la situazione in una variabile globale.
- Il dato da scrivere viene inserito nel record, e poi quest’ultimo viene trascritto nell’archivio; se la scrittura riesce, si restituisce l’esito positivo, altrimenti si azzera l’eventuale annotazione sul campo vuoto e si restituisce un risultato nullo.
Codice.
Int Function ScriveCampo (string sArchivio, string sSezione, string sChiave, string sDato)
Var String sRecord = LeggeRecord (sArchivio, sSezione, sChiave); dichiara e Legge i dati del record
Let gnCampo = CampoGlobale (); controlla l’impostazione del numero di campo
If !StringSegment (sRecord, PIPE, gnCampo) ; se il dato da scrivere non esiste,
&& gnTipo == TERZA Then; e se si sta operando su più campi,
Let gnSalta = TRUE; imposta di saltare la successiva schermata di scelta
EndIf; fine controllo presenza dato
Let sRecord = CambiaCampo (sRecord, sDato, gnCampo); aggiorna il record inserendovi il dato
If ScriveRecord (sArchivio, sSezione, sChiave, sRecord) Then; se la scrittura riesce,
Return TRUE; restituisce l’esito positivo
Else; altrimenti, se la scrittura fallisce,
Let gnSalta = FALSE; cancella l’eventuale indicatore di salto impostato,
Return FALSE; e restituisce un risultato nullo
EndIf; fine controllo scrittura
EndFunction
Esercizio 12.3.19. La funzione AggiornaCampo.
FileScript.
Default.JSS
Nome.
AggiornaCampo
Descrizione.
Gestisce l’aggiornamento dei campi di un record.
Ritorni.
Di tipo Int. L’esito dell’aggiornamento: TRUE per la riuscita, FALSE per il fallimento.
Parametri.
- sNome. L’azione da compiere. Di tipo String.
- sSezione. La sezione dell’archivio in cui scrivere. Di tipo String.
- sChiave. La chiave del record da aggiornare. Di tipo String.
- sDato. L’eventuale dato testuale da inserire nel campo indicato. Di tipo String. Parametro Opzionale.
Novità.
- Le nostre funzioni
ComponeTermine (),
CreaElenco (),
InserisceTesto (),
ConfermaAzioni (),
ChiaveGlobale ()
e
ScriveCampo ().
Fasi.
- Nelle impostazioni iniziali, dapprima si definisce il titolo della finestra di dialogo, servendosi anche della nostra funzione
ComponeTermine ()
, e poi eventualmente si crea un elenco di termini da non inserire, utilizzando la nostra
CreaElenco (). - Si entra poi in un ciclo dove viene chiamata la nostra funzione
InserisceTesto ()
; se dopo l’immissione tale testo è vuoto, o se si è confermata l’uscita dalla fase, il flusso s’interrompe restituendo un risultato nullo. - Se invece del testo è stato immesso, se si stanno elaborando le azioni che devono compiere gli script a gestione personale, si controlla se i termini immessi siano validi, grazie alla nostra
ConfermaAzioni ()
; se così è, si esce dal ciclo, altrimenti viene proposta una conferma tramite la quale si possono aggiungere i termini non riconosciuti all’elenco di quelli validi, oppure si può tornare all’inserimento. - Una volta usciti dal ciclo, dopo aver controllato l’impostazione di una chiave del dato tramite la nostra funzione
ChiaveGlobale ()
, si tenta di salvarlo con la nostra
ScriveCampo ()
. Se l’azione fallisce, viene restituito un risultato nullo, altrimenti sono aggiornati sia il dato della chiave, sia quello sull’ultima voce elaborata, restituendo poi l’esito positivo.
Note.
- Come dimostra il gran numero delle nostre funzioni tra le novità, questo elemento di codice è il vero fulcro del blocco. Per questo motivo, al suo interno sono state controllate due situazioni particolari, che si verificano elaborando solo gli script a gestione personale, e più precisamente:
- nel primo campo, la creazione dell’elenco dei termini vietati;
- Nel quarto campo, il controllo sulla validità di quelli immessi come azioni degli script.
Codice.
Int Function AggiornaCampo (string sNome, string sSezione, ; primi due parametri,
string sChiave, string sDato); seconda riga con gli altri due
Var
String sPrefisso, ; stringa che nel messaggio precede l’oggetto della modifica
String sTesto, ; dato temporaneo
String sSuffisso, ; stringa che segue l’oggetto
String sTitolo, ; titolo per la finestra di input
String sVideo, ; elenco delle voci a video,
String sVietati, ; lista di termini da non inserire
Int iModifica; valore per l’uscita dal ciclo
; determina il prefisso del messaggio, sulla base del tipo di procedura e del campo attivi
Let sPrefisso = StringSegment (lstPrefissiTesto, PIPE, gnTipo + (gnCampo - 1))
; imposta anche il suffisso, con il criterio precedente, formattandovi il termine attivo
Let sSuffisso = FormatString (StringSegment (lstSuffissiTesto, PIPE, gnTipo + (gnCampo -1)), ComponeTermine ())
If !gsTermine Then; se nessun oggetto delle modifiche è stato impostato,
Let gsTermine = gsCategoria; inizializza il termine con la categoria
If gsTermine == MACRO Then; se si stanno elaborando delle macro,
Let gsTermine = gsTermine + gsOggetto; aggiunge anche l’oggetto
EndIf; fine controllo macro
EndIf; fine controllo oggetto delle modifiche
; crea il titolo della finestra d’inserimento testo
Let sTitolo = FormatString (ttlInputDato, sNome, sPrefisso, gsTermine, sSuffisso)
Let sTesto = sDato; duplica il testo inserito per il successivo confronto
If gnCampo == PRIMA Then; se si è nella schermata iniziale,
Let sVideo = StringLower (CreaElenco (gsArchivio, sSezione)); rileva le voci da non immettere,
Let sVietati = CambiaCampo (sVideo, StringLower (sDato), FALSE, BELL); e vi toglie il dato
EndIf; fine controllo campo
While !iModifica; continua finché il valore rimane a zero
Let sTesto = InserisceTesto (sTitolo, sTesto, gnMultiple, sVietati); memorizza il dato eventualmente immesso
SpeechOff (); spegne comunque la sintesi,
Let gnStato = TRUE; e ne imposta la riattivazione programmata
If !sTesto ; se nessun dato è presente,
|| sTesto == sDato Then; oppure, se il dato non è stato modificato,
Return FALSE; restituisce un risultato nullo
EndIf; fine controllo presenza dato
Let iModifica = TRUE; imposta il valore per l’uscita dal ciclo
If gsCategoria == SCRIPTS ; se si stanno elaborando gli script personali,
&& gnCampo == QUARTA Then; e si stanno modificando le azioni da compiere,
If !ConfermaAzioni (sTesto) Then; se i dati inseriti non sono stati riconosciuti,
Let iModifica = FALSE; annulla il valore per tornare all’inserimento
EndIf; fine controllo tasti validi
EndIf; fine controllo campo
EndWhile; fine ciclo modifica
If !sChiave Then; se una chiave non è stata specificata,
Let sChiave = ChiaveGlobale (); prende il valore dall’eventuale variabile, o dalla prima chiave nell’elenco
EndIf; fine controllo chiave
If !ScriveCampo (gsArchivio, sSezione, sChiave, sTesto) Then; se la scrittura fallisce,
Return FALSE; restituisce un risultato nullo
Else; altrimenti, se il dato è trascritto,
If gnCampo == PRIMA Then; se si è nella schermata iniziale,
Let gsChiave = sChiave; allinea il dato utilizzato,
; e aggiorna anche il puntatore dell’ultima voce elaborata
ScriveRecord (gsArchivio, sSezione, ZERO, gsChiave)
EndIf; fine controllo campo
Return TRUE; restituisce l’esito positivo
EndIf; fine controllo scrittura
EndFunction
Collaudo.
- Se la compilazione va a buon fine, c’è da rendere opzionale l’ultimo parametro dei quattro.
Esercizio 12.3.20. La funzione CercaVoci.
FileScript.
Default.JSS
Nome.
CercaVoci
Descrizione.
Rileva le voci di dati registrate nella sezione e nell’archivio della categoria specificata, consentendo di avviare l’inserimento della prima voce, qualora non ve ne siano ancora di presenti.
Ritorni.
Di tipo Int. TRUE, per una rilevazione corretta dei dati; FALSE, per un qualsiasi errore riscontrato.
Parametri.
- sSezione. La sezione dell’archivio dove elaborare i dati. Di tipo String.
- sSuffisso. Il suffisso numerico in forma testuale. Di tipo String.
Novità.
- Le nostre funzioni
ProsegueFlusso ()
e
AggiornaCampo (). - Le costanti numeriche
ALTO
e
CATTURA
, entrambe equivalenti al valore 1, che rappresentano, nell’ordine, la posizione superiore e la fase di acquisizione dei tasti premuti. - La costante
AGGIUNGE
, che invece corrisponde all’omonimo termine.
Fasi.
- Si tenta dapprima di rilevare le chiavi dei dati attivi registrati; se dei dati non esistono, oppure se manca la chiave Zero, si tenta di scrivere almeno quest’ultima; se tale scrittura fallisce, il flusso s’interrompe restituendo un risultato nullo.
- Sono poi eseguiti impostazioni e controlli sempre sulla chiave e sul campo attivi, dopo dei quali si tenta di leggere il dato; se questo già esiste, o ci si trova nell’ultimo campo degli script a gestione personale, il flusso s’interrompe restituendo un risultato positivo.
- Se ancora si stanno elaborando gli script personali, e ci si trova nel secondo campo, si attiva l’attesa dei tasti e si interrompe il flusso con un risultato nullo.
- In tutti gli altri casi in cui il dato non esista, si avvia l’immissione di un nuovo dato, casomai chiedendone conferma; se questo inserimento è valido, sono aggiornati gli elenchi delle voci ed il contatore delle stesse, restituendo poi l’esito positivo; se invece l’immissione è annullata, viene restituito un risultato nullo.
Note.
- Se quello precedente era lo strumento attivo principale, questa funzione è quella che gestisce i controlli e l’eventuale inserimento di nuovi dati; essa è chiamata sia nelle fasi di utilizzo dei dati inseriti tramite le procedure, sia durante gli aggiornamenti delle procedure stesse: proprio per questo, si riuscirà a comprenderne il reale funzionamento solo più avanti, quando avremo realizzato le procedure che inseriscono ed aggiornano le stringhe di cui la funzione controlla l’esistenza.
Codice.
Int Function CercaVoci (string sSezione, string sSuffisso)
Var String sChiavi; elenco delle chiavi rilevate
Let sChiavi = ChiaviUtente (gsArchivio, sSezione); rileva i dati registrati
If !sChiavi ; se nessuna chiave è ancora registrata nella sezione,
|| !StringSegmentIndex (sChiavi, PIPE, ZERO, TRUE) Then; oppure, se la chiave testuale Zero non è ancora stata registrata,
If !ScriveNumerico (gsArchivio, sSezione, ZERO, FALSE) Then; se la scrittura fallisce,
SayFormattedMessage (OT_ERROR, hlpErroreDato, SALVA); formatta e pronuncia l’avviso,
Return FALSE; e interrompe il flusso, restituendo un risultato nullo
EndIf; fine controllo scrittura chiave Zero
EndIf; fine controllo chiavi
Let gsElenco = CambiaCampo (sChiavi, ZERO); rende globale l’elenco delle chiavi, senza la zero
Let gnTotali = StringSegmentCount (gsElenco, PIPE); conta i campi ancora presenti
Let gsChiave = ChiaveGlobale (); imposta o ribadisce il dato registrato
Let gnCampo = CampoGlobale (); controlla l’impostazione del numero di campo
If LeggeCampo (gsArchivio, sSezione, gsChiave) ; se esiste un dato per il campo,
|| (gsCategoria == SCRIPTS ; oppure, se si stanno elaborando gli script personali,
&& gnCampo == gnMaxCampi) Then; e ci si trova nell’ultimo campo,
Return TRUE; restituisce l’esito positivo
ElIf gsCategoria == SCRIPTS; se si stanno solo elaborando gli script Personali,
&& gnCampo == SECONDA Then; ma si è nel secondo campo,
suona (Nota (gnCampo - 1), RITARDA); emette una nota ascendente
Let gnFerma = CATTURA; imposta il valore per l’attesa dei tasti,
Return FALSE; e torna alla funzione chiamante
Else; altrimenti, negli altri casi in cui manca il dato,
If gnTipo != TERZA ; se non si stanno elaborando più campi,
|| gnCampo == PRIMA Then; oppure, se si è in ogni caso al primo campo,
If !ProsegueFlusso (gsCategoria, sSuffisso) ; se non si vuole proseguire,
Return FALSE; interrompe il flusso, restituendo un risultato nullo
EndIf; fine controllo conclusione
EndIf; fine controllo campo valido
suona (Nota (gnCampo - 1), RITARDA); emette una nota ascendente
If AggiornaCampo (AGGIUNGE, sSezione, gsChiave, NULLO); se l’aggiornamento riesce,
Pause ()
Let sChiavi = ChiaviUtente (gsArchivio, sSezione); rileva nuovamente i dati registrati, rimuovendo la chiave zero
Let gsElenco = CambiaCampo (sChiavi, ZERO);
Let gnTotali = StringSegmentCount (gsElenco, PIPE); quindi, riconta i campi dell’elenco,
Return TRUE; e restituisce l’esito positivo
EndIf; fine controllo aggiornamento
EndIf; fine controllo dato
Return FALSE; se nessun altro controllo ha interrotto il flusso, restituisce un risultato nullo
EndFunction
***
Muoversi di caratteri o pixel.
Questa breve sezione è composta da una coppia di funzioni, le quali hanno come effetto lo spostarsi del cursore di un carattere avanti o indietro. Il loro compito è però anche quello di valutare come Jaws riesca a leggere la posizione del cursore, ed in particolare se sulla base del numero di caratteri, di pixel, oppure in nessun modo.
Per fare questo, Jaws utilizza Le funzioni
GetCursorRow ()
e
GetCursorCol ()
, che se ricordate servono a rilevare, nell’ordine, il valore della posizione di riga e colonna. Se a tali funzioni non si indicano parametri, in primo luogo esse tenteranno di rilevare la posizione nel numero di caratteri e solo dopo, se il primo tentativo fallisce, restituiranno il valore in pixel.
Il problema è che la posizione di riga e di colonna, se espressa in caratteri, può avere nel documento un valore assoluto, mentre i pixel indicano sempre e solo la posizione relativa del cursore nello schermo.
Se volete capire meglio la differenza tra i due sistemi, seguite questa rapida prova:
- Aprite il file script Predefinito,
Default.JSS
, e portatevi alla fine del file. - Eseguite il nostro script per leggere il numero della riga corrente, premendo i tasti
Control+J
, e sarà pronunciato un numero di righe ben superiore alle 20000. - Ora uscite dall’Editor di Script, e portatevi nella cartella delle impostazioni per l’Utente. Qui selezionate il file script di prima
Default.JSS
, ma non cliccatevi sopra con Invio, bensì agite sul comando
"Apri con..."
del menu contestuale. - Qui selezionate il Blocco Note e, una volta apertosi il file, andate ancora alla sua fine. Se agite ora sulla combinazione
Control+J
, vi sarà letto un valore variabile, dell’ordine di qualche centinaia, che rappresenta appunto i pixel della posizione relativa del cursore nello schermo.
Questo succede perché nel Blocco Note, con documenti di grandi dimensioni, Jaws non riesce più a rilevare la posizione in caratteri, per righe e colonne, e passa appunto a restituirli per pixel. In questi particolari casi, tuttavia, possiamo tornare ad usare la lettura tramite la Bara di Stato, che, a patto abbiate ancora disponibili le impostazioni predisposte nel nono capitolo, può fornirci i dati a noi necessari.
A tal fine, la nostra funzione farà un piccolo test, cercando di leggere la posizione con questo sistema. Se ci riesce, imposta il valore 1 ad una variabile globale, indicando così agli altri elementi di codice di prelevare i dati tramite la nostra funzione
InfoStato ().
Se per far agire questa funzione servisse reimpostare il separatore dei dati, che ricordiamo essere nel Blocco Note il carattere Virgola, la procedura consentirà di inserirlo direttamente. Se in ogni caso i dati non fossero configurati, o non consentissero di leggere riga e colonna tramite la Barra di Stato, la situazione sarebbe annotata sempre sulla stessa variabile globale, ponendovi il valore 2.
Importante notare che, anche nel caso fosse impossibile rilevare i dati precisi sulla posizione del cursore, la procedura riuscirà comunque a funzionare. Essa, infatti, si muove come faremmo noi con le frecce, in alto ed in basso, o a destra e sinistra, e l’unico dato che serve veramente è sapere, quando si trova un elemento cercato, se ci si trovi o meno ad inizio riga.
Di questo si occupa in particolare il primo elemento di codice del blocco, sfruttando il risultato della funzione nativa
GetPriorCharacter
, (OttieniCaratterePrecedente). Tale funzione di Jaws, infatti, non restituisce sempre il solo carattere che precede il cursore, come starebbe ad indicare la traduzione, bensì a volte anche una vera e propria stringa; tale stringa testuale, in alcuni applicativi contiene anche uno spazio, oltre al carattere che precede, oppure i caratteri di controllo per il Ritorno a Capo o la Nuova Linea, quando si è posizionati nella prima colonna.
E proprio questa sarà la caratteristica che andremo ad utilizzare di tale funzione, la quale avrebbe anche un parametro facoltativo, ma che non serve specificare. Essa, dunque, qui di seguito sarà inserita in una struttura con alcuni controlli, i quali restituiscono un valore positivo quando si è ad inizio riga, ed un risultato nullo in tutti gli altri casi.
Esercizio 12.4.1. La funzione InizioRiga.
FileScript.
Default.JSS
Nome.
InizioRiga
Descrizione.
Determina se il cursore attivo si trova all’inizio della riga corrente.
Ritorni.
Di tipo Int. TRUE, per il cursore attivo posto a inizio riga; FALSE, per qualsiasi altra posizione del cursore.
Novità.
- La funzione integrata
GetPriorCharacter ()
, già illustrata nella premessa.
Codice.
Int Function InizioRiga ()
Var String s; singolo carattere
; rileva il carattere iniziale della stringa, ottenuta controllando quello precedente
Let s = StringLeft (GetPriorCharacter (), PRIMA)
If !s ; se il carattere è vuoto,
|| s == CR; oppure, è un Ritorno a Capo,
|| s == LF Then; o, infine, un carattere di Nuova Linea,
Return TRUE; restituisce l’esito positivo
Else; altrimenti,
Return FALSE; restituisce un risultato nullo
EndIf; fine controllo posizione cursore
EndFunction
Esercizio 12.4.2. La funzione MisuraMovimento.
FileScript.
Default.JSS
Nome.
MisuraMovimento
Descrizione.
Muove il cursore in avanti o indietro di un carattere, sulla base del valore indicato nel primo parametro, determinando se nel documento corrente la procedura riesce a rilevare la posizione del cursore come colonne e righe.
Ritorni.
Di tipo Int. TRUE, per un movimento sulla base di colonne e righe; FALSE, per la rilevazione in un’altra unità di misura, o per l’arrivo agli estremi di un documento.
Parametri.
- iVerso. La direzione del movimento del cursore: 1, per AVANTI; 2, per INDIETRO. Di tipo Int.
- x. Per Riferimento. La posizione orizzontale del cursore dopo lo spostamento. Di tipo Int.
Novità.
- La costante
AVANTI
, di valore 1, che indica lo spostamento del cursore verso destra.
Fasi.
- Come impostazioni iniziali, si spegne la sintesi, e si rilevano i valori di riga e colonna di partenza.
- Una prima struttura di controllo sposta il cursore di un carattere avanti o indietro, a seconda del valore specificato come parametro.
- Dopo aver resettato la schermata , si rilevano nuovamente i valori di riga e colonna, e si riattiva la sintesi vocale.
- Una seconda struttura confronta i valori memorizzati, sulla base della direzione del movimento; in particolare, si controlla se il cursore risulta essersi mosso di una sola unità, oppure, se così non è, se si riescano a leggere i dati dalla Barra di Stato, impostando in una variabile globale il valore che indichi l’esito del controllo.
- Sempre nel caso di movimento del cursore, si controlla se ci si trovi sulla prima colonna, tramite la nostra funzione
InizioRiga ()
, casomai trasmettendo per riferimento il valore 1, come colonna di partenza della successiva ricerca, a prescindere dal tipo di rilevazione dei caratteri effettuata; al termine del controllo, viene comunque restituito un valore positivo. - Se il cursore invece non si è mosso, si pronuncia l’avviso di aver raggiunto uno dei due limiti del documento, restituendo poi un risultato nullo.
Codice.
Int Function MisuraMovimento (int iVerso, int ByRef x)
Var
Int iStato, ; stato di attivazione della sintesi
Int c, ; prima rilevazione colonna
Int r, ; prima rilevazione riga
Int y, ; seconda rilevazione riga
String s; primo carattere della stringa ottenuta rilevando quello precedente
Let iStato = ControllaSintesi (FALSE); spegne la sintesi, memorizzandone lo stato
Let c = ValoreColonna (); registra il numero della colonna di partenza
Let r = ValoreRiga (); memorizza anche il numero di riga
If iVerso == AVANTI Then; Se ci si deve muovere in avanti,
NextCharacter (); chiama la funzione nativa
Else; altrimenti, per muoversi all’indietro,
PriorCharacter (); chiama l’altra funzione originale
EndIf; fine controllo direzione
Refresh (); resetta la schermata
Let x = ValoreColonna (); registra la nuova posizione orizzontale
Let y = ValoreRiga (); memorizza anche la nuova posizione verticale
ControllaSintesi (iStato); ripristina lo stato di attivazione precedente
If x != c ; se il cursore si è mosso in orizzontale,
|| y != r Then; oppure, se ha soltanto cambiato riga,
If (r == y && (x == (c +1) || x == (c - 1))) ; se la riga non è cambiata, e la colonna è diversa d 1,
|| (x == c && (r == (y + 1) || r == (y - 1))) Then; o invariata è la colonna, e la riga è spostata di 1,
Let gnRilevamento = FALSE; azzera il valore sulle modalità di rilevazione
ElIf InfoStato (RIGA, TRUE) Then; se invece i dati si possono rilevare dalla Barra di Stato,
Let gnRilevamento = PRIMA; annota di usare tale metodo
Else; altrimenti, se non si può rilevare la posizione orizzontale,
Let gnRilevamento = SECONDA; imposta di portare comunque il cursore ad inizio riga
EndIf; fine controllo modalità rilevazione
If InizioRiga (); se ci si trova nella prima colonna,
Let x = 1; omologa la posizione di partenza, a prescindere dalla modalità di rilevazione
EndIf; fine controllo carattere precedente
Return TRUE; in ogni caso, restituisce l’esito positivo
Else; altrimenti, se il cursore non si è mosso,
Suona (ERRORE1); emette un segnale acustico
If iVerso == AVANTI Then; se ci si doveva spostare in avanti,
SayMessage (OT_ERROR, hlpFineFile, hlpFineFile_corto); pronuncia l’avviso,
Else; altrimenti, se ci si doveva muovere all’indietro,
SayMessage (OT_ERROR, hlpInizioFile, hlpInizioFile_corto); pronuncia l’avviso,
EndIf; fine controllo direzione
Return FALSE; in ogni caso, restituisce un risultato nullo
EndIf; fine controllo movimento cursore
EndFunction
***
La ricerca e l’avvio programmato delle funzioni.
Giunti alla penultima sezione del capitolo, affrontiamo ora il cuore della procedura, quello che si occupa di scorrere il documento e, su ciascuna riga, di cercare le stringhe impostate.
Le ricerche successive, che si susseguono una dopo l’altra finché un elemento non sia stato trovato, sono rese possibili da un
avvio programmato
delle funzioni. Questo sistema consente di definire dopo quanti decimi di secondo ciascun elemento di codice deve essere chiamato, sospendendo così il solito schema delle istruzioni eseguite l’una dopo l’altra.
Nel nostro caso, noi iniziamo lanciando la ricerca delle stringhe, stabilendo un tempo limite di tre secondi. Trascorso questo tempo, se nessuna stringa è stata trovata, la funzione di ricerca viene riavviata, e così via, sino al verificarsi di uno di questi tre possibili eventi:
- Il ritrovamento di una stringa.
- La pressione del tasto Escape per interrompere la ricerca.
- L’arrivo ad uno degli estremi del documento.
Le funzioni che regolano questo sistema sono due:
-
ScheduleFunction
, (ProgrammaFunzione). -
UnScheduleFunction
, (DeProgrammaFunzione).
Come si può capire dalle traduzioni, la prima serve ad impostare la programmazione, la seconda a cancellarla.
Nel dettaglio,
ScheduleFunction ()
restituisce un valore progressivo con il quale Jaws identifica quel tipo di programmazione. Per questo, di solito il risultato della funzione va assegnato ad una variabile numerica, la quale viene poi usata per identificare l’avvio programmato da cancellare. Per programmare un avvio, alla funzione si devono specificare due parametri:
- Il nome della funzione da avviare, che va inserito come un testo tra virgolette, oppure tramite costanti o variabili, usando il solo nome senza specificare eventuali parentesi.
- Il numero dei decimi di secondo dopo i quali si vuole che l’a funzione sia avviata.
Nelle ultime versioni di Jaws la funzione avrebbe anche un terzo parametro opzionale , che consente di interrompere la programmazione al verificarsi di alcuni eventi o premendo un tasto. Pur essendo un aspetto che dovremo implementare nel nostro sistema, eviteremo di usare questo ulteriore parametro al fine di rendere la riga di codice compatibile anche con versioni di Jaws meno recenti.
Per quel che riguarda
UnScheduleFunction ()
, invece, essa ha come unico parametro proprio il valore numerico che identifica la programmazione da cancellare. Per evitare che ci possano essere degli avvii inopportuni della funzione, la prassi consiglia di usare sempre una struttura di controllo con tale funzione prima di impostare l’avvio vero e proprio, usando il seguente schema:
If ValoreIdentificativo Then; se un avvio programmato è attivo,
UnScheduleFunction (ValoreIdentificativo); cancella la precedente impostazione
EndIf; fine controllo avvii programmati
; imposta il nome della funzione ed il tempo dopo il quale avviarla
Let ValoreIdentificativo = ScheduleFunction (NOME_FUNZIONE, NUMERO_DECIMI)
Quindi, solitamente, prima ci si accerta di cancellare eventuali avvii programmati di un certo tipo, poi casomai si impostano, o reimpostano, tali avvii.
Ovviamente, nel caso si debba soltanto cancellare una programmazione, si potrà usare la sola
UnScheduleFunction ()
, avendo cura di porvi come parametro il valore che la identifica.
Il motore della ricerca.
Così come si accennava, nelle prossime funzioni sono raccolte il vero cuore operativo della procedura e, quindi, dell’intero capitolo. In particolare, l’ultima di queste sarà la funzione programmata, quella che viene avviata da
ScheduleFunction ()
, al servizio della quale operano tutte le altre.
A tal proposito, la struttura di controllo, che nell’esempio di prima coinvolgeva
UnScheduleFunction ()
, l’abbiamo racchiusa in una funzione a sé stante, sia per semplificare il codice, sia per potercene servire anche in ulteriori utilizzi dell’avvio programmato in altre procedure. Per favorire il dialogo tra gli elementi, senza l’uso di parametri, abbiamo usato una variabile globale per memorizzare il valore che identifica l’eventuale avvio programmato.
Per quanto riguarda le altre funzioni, la prima serve soltanto a gestire la pronuncia dei messaggi durante la ricerca, che può essere la percentuale del documento o il numero di righe. La seconda di questo blocco, invece, è il vero e proprio strumento di ricerca, che controlla le singole righe di testo, passate alla funzione come parametro, se contengano o meno le stringhe da trovare.
Esercizio 12.5.2. La funzione PosizioneDocumento.
FileScript.
Default.JSS
Nome.
PosizioneDocumento
Descrizione.
Pronuncia lo stato di avanzamento della ricerca, leggendo la percentuale elaborata del documento corrente, se rilevabile, oppure il numero della riga sul cursore.
Ritorni.
Di tipo Void. Nessuno.
Fasi.
- Dopo aver spento comunque la sintesi, controlla che ci si trovi al primo passaggio; se così è, viene pronunciato un avviso che indica di poter fermare la ricerca premendo il tasto Escape, valorizzando poi la variabile globale che ne impedisce la rilettura.
- Dal secondo passaggio in poi, si tenta di rilevare la percentuale della posizione del cursore rispetto al documento; se un valore è stato rilevato, lo si pronuncia, altrimenti sarà letto il numero della riga sul cursore.
Codice.
Void Function PosizioneDocumento ()
Var Int iPosiz; percentuale nel documento corrente
SpeechOn (); riattiva la sintesi
If !gnPrimo Then; se si è al primo passaggio,
SayMessage (OT_ERROR, hlpPremiEsc); avvisa di fermare la ricerca premendo il tasto Escape,
Let gnPrimo = TRUE; e imposta il valore per impedirne la rilettura
Else; altrimenti,
Let iPosiz = GetDocumentPercentage (); rileva la posizione in percentuale rispetto al documento
If iPosiz Then; se una percentuale è stata rilevata,
; formatta e pronuncia il valore rilevato
SayFormattedMessage (OT_ERROR, msgPercento, NULLO, iPosiz)
Else; altrimenti,
; formatta e pronuncia il numero della riga corrente
SayFormattedMessage (OT_ERROR, msg1, NULLO, gnValore)
EndIf; fine controllo percentuale
EndIf; fine controllo avvisi
SpeechOff (); spegne la sintesi
EndFunction
Esercizio 12.5.3. La funzione CercaElementi.
FileScript.
Default.JSS
Nome.
CercaElementi
Descrizione.
Scorre gli elementi da cercare, restituendo il primo elemento trovato nel testo passato come parametro.
Ritorni.
Di tipo String. L’elemento di ricerca trovato.
Parametri.
- sTesto. Il testo in cui cercare. Di tipo String.
- iPartenza. Se valorizzato, indica la colonna da cui è partita la ricerca, nel caso non sia la prima. Di tipo Int.
Novità.
- La costante
RICERCA_INTERNA
, dove è memorizzato un carattere speciale per indicare le stringhe che devono essere cercate all’interno del testo, non dal suo inizio, che nel nostro caso è il carattere E Commerciale. - Le costanti
INIZIO
e
INTERNO
, equivalenti a dei valori numerici, rispettivamente 3 e 4.
Fasi.
- Un ciclo scorre le stringhe da cercare, memorizzate in una variabile globale.
- La singola stringa, o elemento di ricerca, viene analizzato per impostare il tipo di ricerca da effettuare, valutando se si tratti di un testo da cercare a partire dall’inizio della riga, oppure se vada trovato al suo interno; in quest’ultimo caso, oltre ad impostare il tipo, viene ripulito anche l’elemento di ricerca dal carattere indicatore.
- Una seconda struttura di controllo effettua materialmente le ricerche, sfruttando le funzioni native di Jaws, sulla base del tipo impostato; quando un elemento viene trovato, il flusso s’interrompe, restituendolo.
- Se il ciclo si conclude senza aver individuato alcun elemento, viene restituito un risultato nullo.
Codice.
String Function CercaElementi (string sTesto, int iPartenza)
Var
Int i, ; contatore del ciclo
String sElemento, ; stringa da cercare
Int iTipo; modalità di ricerca
For i = 1 To gnTotali; scorre gli elementi rilevati
Let sElemento = StringSegment (gsElenco, BELL, i); estrae il singolo termine da cercare
; controlla che il primo carattere dell’elemento non sia quello che indica di cercare dentro alla riga
If !StringStartsWith (sElemento, RICERCA_INTERNA) Then; se tale carattere non è,
Let iTipo = INIZIO; imposta di cercare ad inizio riga
Else; altrimenti, se il carattere segnalatore è presente,
Let iTipo = INTERNO; imposta il valore per la ricerca all’interno,
Let sElemento = StringChopLeft (sElemento, StringLength (RICERCA_INTERNA)); e lo rimuove
EndIf; fine controllo tipo ricerca
If iPartenza <= PRIMA ; se la ricerca può partire dalla prima colonna,
&& iTipo == INIZIO Then; ed è da lì che bisogna cercare,
If StringStartsWith (sTesto, sElemento) Then; se il termine è coincide,
Return sElemento; restituisce l’elemento trovato
EndIf; fine controllo presenza all’inizio
ElIf iTipo == INTERNO Then; se l’elemento va cercato invece dentro alla riga,
If StringContains (sTesto, sElemento) Then; se l’elemento è stato trovato,
Return sElemento; lo restituisce
EndIf; fine controllo ritrovamento interno
EndIf; fine controllo ritrovamenti
EndFor; fine scansione elementi
Return NULLO; se nessun altro controllo è intervenuto, restituisce una stringa vuota
EndFunction
Esercizio 12.5.4. La funzione AnnullaProgrammato.
FileScript.
Default.JSS
Nome.
AnnullaProgrammato
Descrizione.
Annulla un eventuale avvio programmato di una funzione.
Ritorni.
Di tipo Void. Nessuno.
Novità.
- La funzione integrata
UnScheduleFunction ()
, già illustrata nella premessa.
Note.
- La struttura di controllo che compone la funzione è in sostanza la stessa che abbiamo proposto nell’esempio d’utilizzo del sistema citato nella premessa, nella parte che contiene la funzione integrata
UnScheduleFunction ()
; in più, abbiamo posto solo un’istruzione che annulla il valore dell’avvio programmato, in questo caso memorizzato in una variabile globale, anche per segnalare agli altri elementi di codice l’assenza di questo tipo d’impostazione.
Codice.
Void Function AnnullaProgrammato ()
If gnProgrammato Then; se l’avvio programmato era stato impostato,
UnScheduleFunction (gnProgrammato); lo annulla,
Let gnProgrammato = FALSE; e resetta il valore
EndIf; fine controllo programmazione
EndFunction
Esercizio 12.5.5. La funzione SpostaAElemento.
FileScript.
Default.JSS
Nome.
SpostaAElemento
Descrizione.
Muove il cursore alla colonna in cui inizia l’elemento trovato.
Ritorni.
Di tipo Void. Nessuno.
Parametri.
- sElemento. Il testo cui spostare il cursore. Di tipo String.
- iPartenza. La colonna di partenza della ricerca. Di tipo Int.
Novità.
- La funzione integrata
SayFromCursor
, (LeggiDalCursore); Pronuncia il testo a partire dal cursore sino al termine della riga corrente. - La costante
ARRIVO
, la quale contiene il nome di un file audio usato per segnalare lo spostamento al punto desiderato.
Fasi.
- Le prime due strutture di controllo servono a calcolare la colonna sulla quale dovrebbe posizionarsi il cursore dopo aver trovato l’elemento, dato che ci si dovrebbe comunque trovare nella riga corretta.
- La successiva struttura rileva invece la colonna corrente del cursore, tramite la funzione nativa di Jaws, oppure leggendo dalla Barra di Stato, sulla base del valore impostato nell’apposita variabile globale.
- Se non si riesce a leggere la posizione del cursore, un controllo consente casomai di portarsi ad inizio riga; se invece la posizione corrente e quella definitiva sono diverse, viene avviato un ciclo che si sposta di tanti caratteri quanti sono quelli di differenza tra le due posizioni, in avanti o indietro a seconda del valore positivo o negativo del valore di confronto.
- In ogni caso, viene emesso un segnale acustico, riattivata la sintesi e pronunciato il testo a partire dalla colonna individuata sino a fine riga, tramite la funzione integrata
SayFromCursor ().
Codice.
Void Function SpostaAElemento (string sElemento, int iPartenza)
Var
Int iPosiz, ; posizione dell’elemento da sinistra nella riga
Int iContraria, ; posizione dell’elemento da destra
Int iAttuale, ; posizione corrente del cursore
Int i; contatore del ciclo
If gnVerso == AVANTI Then; se è attiva la ricerca verso il basso,
; registra la posizione dell’elemento, aggiungendo il valore di partenza
Let iPosiz = iPartenza + StringContains (gsRiga, sElemento)
Else; altrimenti, se si sta cercando verso l’alto,
; registra la posizione, invertendo la stringa in cui cercare
Let iContraria = StringContains (StringReverse (gsRiga), StringReverse (sElemento))
; calcola l’effettiva posizione del cursore, partendo da sinistra
Let iPosiz = StringLength (gsRiga) - ((iContraria + StringLength (sElemento)) - 2)
EndIf; fine controllo posizione
If !iPosiz Then; se per qualche motivo il valore è a zero,
Let iPosiz = PRIMA; imposta il valore alla prima colonna
EndIf; fine controllo colonna finale
If !gnRilevamento Then; se il dato può essere rilevato direttamente,
Let iAttuale = ValoreColonna (); memorizza il numero della colonna corrente
ElIf gnRilevamento == 1 Then; se invece si è impostata la prima modalità alternativa,
Let iAttuale = StringToInt (InfoStato (COLONNA, IsSameScript ())); lo prende dalla Barra di Stato
EndIf; fine controllo rilevazione dati
If gnRilevamento == SECONDA Then; se si è impostato di portarsi comunque ad inizio riga,
If !InizioRiga () Then; se non si è già sulla prima colonna,
JawsHome (); si sposta ad inizio riga
EndIf; fine controllo posizione cursore
ElIf iAttuale != iPosiz Then; se invece la posizione corrente e quella definitiva sono diverse,
For i = 1 To Abs (iAttuale - iPosiz); esegue il numero assoluto degli spostamenti
If iAttuale < iPosiz Then; se la posizione corrente è minore della definitiva,
NextCharacter (); si muove di un carattere in avanti
Else; altrimenti,
PriorCharacter (); torna di un carattere all’indietro
EndIf; fine controllo direzione
EndFor; fine ciclo movimenti
EndIf; fine controllo posizioni
Suona (ARRIVO); avvisa dello spostamento
SpeechOn (); riattiva la sintesi
SayFromCursor (); legge dal cursore sino a fine riga
EndFunction
Esercizio 12.5.6. La funzione ScorreRighe.
FileScript.
Default.JSS
Nome.
ScorreRighe
Descrizione.
Gestisce la scansione delle righe nel documento, cui sottoporre la ricerca degli elementi preventivamente registrati; il suo lavoro potrà concludersi impostando il riavvio programmato di sé stessa, nel caso di mancato ritrovamento, o fermando la ricerca, nel caso di pressione del tasto Escape.
Ritorni.
Di tipo Void. Nessuno.
Parametri.
- iPartenza. La colonna da cui è partita la ricerca degli elementi. Di tipo Int.
Novità.
- Le nostre funzioni
PosizioneDocumento (),
CercaElementi (),
AnnullaProgrammato ()
e
SpostaAElemento (). - La funzione integrata
ScheduleFunction ()
, già presentata nella premessa. - La funzione integrata
NextLine ()
, che già avevamo incontrato nel secondo capitolo, ma che soltanto ora inseriamo nel nostro codice. Se ricordate, il suo compito è quello di spostare il cursore alla riga sottostante; ha un parametro opzionale, con il quale si può indicare da quale periferica, tastiera, Barra Braille o Mouse, questo comando sia impartito, ma in genere non serve indicarlo. - La costante testuale
SCORRE_RIGHE
, che contiene il nome di questa stessa funzione, e che dal suo interno viene posta come il soggetto cui sottoporre l’avvio programmato. - Le costanti numeriche
INDIETROMAX_RICERCA
e
MAX_RIGHE
, equivalenti nell’ordine ai numeri 2, 3000 e 10, il primo indicante la direzione all’indietro, il secondo quanti millesimi deve durare al massimo un ciclo di ricerca, ed il terzo il numero massimo di righe consecutive che devono essere registrate per determinare di essere giunti ad una delle estremità del documento.
Fasi.
- Come impostazioni iniziali, si fa pronunciare un messaggio, chiamando la nostra funzione
PosizioneDocumento ()
,quindi si rileva la posizione corrente del cursore, si cancella l’eventuale impostazione precedente con la nostra
AnnullaProgrammato ()
, e si predispone un nuovo avvio della funzione dopo circa tre secondi, tramite la funzione nativa
ScheduleFunction (). - Inizia poi il ciclo di ricerca, che terminerà quando sarà trovato un elemento testuale, e e quindi rimosso l’avvio programmato, oppure quando sarà trascorso il tempo massimo consentito.
- La riga sul cursore viene così acquisita, e poi ripulita da caratteri e spazi inutili; se ancora non vuota, il suo contenuto viene passato alla funzione di ricerca vera e propria, la nostra
CercaElementi (). - Se la ricerca ha avuto buon esito, si cancella nuovamente il riavvio, interrompendo così il ciclo, e l’elemento testuale restituito viene passato all’altra nostra funzione,
SpostaAElemento ()
, che posiziona il cursore alla colonna dove è stato trovato, leggendone infine la parte rimanente di riga. - Se invece non si è rilevato alcun elemento, dapprima si controlla che non sia stato premuto il tasto Escape, che causerebbe l’interruzione della ricerca; se così non è, ci si sposta alla riga successiva o precedente, tramite le funzioni integrate
NextLine ()
e
PriorLine ()
, dove la riga corrente viene rilevata e portata in minuscolo per il confronto. - Viene quindi confrontata la posizione corrente del cursore con quella precedente e, qualora ci si sia mossi di una riga in basso o in alto, il ciclo continua; altrimenti, si annota tale ripetizione in una variabile globale; quando questa variabile, dopo una serie di righe uguali, raggiunge il valore massimo stabilito, si dà per assunto che si sia giunti ad uno degli estremi del documento, impostando il relativo valore ed interrompendo il ciclo.
- Una volta terminato il ciclo, qualora non sia programmato alcun riavvio della funzione, se si è alle estremità del documento, viene letto un appropriato messaggio; altrimenti, viene pronunciato solo il contenuto della riga corrente.
- Se, infine, la funzione deve riavviarsi dopo il tempo programmato, il flusso termina senza alcuna altra azione, nell’attesa di una nuova chiamata.
Note.
- Nel caso in cui non si trovi un elemento, il nuovo avvio programmato non è la conseguenza della fine del ciclo di ricerca: il fatto che l’uno finisca quando inizia l’altro, ovvero la sincronizzazione tra essi, viene creata dall’uso dello stesso valore, quello della costante
"MAX_RICERCA"
, sia per i decimi di ritardo dell’avvio programmato, divisi per il valore 100, sia per i millesimi trascorsi dall’inizio del ciclo, confrontati con il risultato della funzione nativa
GetTickCount (). - a proposito del valore reimpostato nella costante
"MAX_RICERCA"
, se ne è usato uno in millesimi per avere un confronto diretto con il risultato della citata funzione nativa, che viene effettuato ad ogni iterazione del ciclo, mentre la divisione per 100 dell’altro valore viene effettuata solo ad ogni nuova chiamata della funzione, e corrisponde ai tre secondi di ciclo massimo citati nella premessa. - Le righe consecutive che si devono contare per causare l’interruzione del lavoro sono inizialmente impostate a 10; è comunque in teoria possibile che anche dentro ad un documento vi siano una serie di almeno dieci righe consecutive, ad esempio vuote, ed in questo caso la procedura si fermerebbe anche senza aver raggiunto i limiti del documento; qualora si sia soliti utilizzare documenti con queste caratteristiche, è sempre possibile aumentare il numero di ripetizioni, anche se questo crea un ritardo nel cogliere l’eventuale fine reale del documento stesso.
Codice.
Void Function ScorreRighe (int iPartenza)
Var
Int iStart, ; tempo d’inizio della procedura
String sTesto, ; testo senza spazi e tabulazioni
String sElemento, ; singolo elemento di ricerca
Int iEstremi; indicatore di raggiunto limite del documento
If !iPartenza Then; se non si è all’inizio della ricerca,
PosizioneDocumento (); legge la percentuale o le righe del documento corrente, oppure altri tipi di avviso
ElIf gnVerso == INDIETRO Then; se in ogni caso la ricerca è verso l’alto,
Let iPartenza = FALSE; resetta il valore
EndIf; fine controllo parametri
Let iStart = GetTickCount (); registra il momento d’inizio della procedura
; lancia l’eventuale nuovo avvio programmato della funzione
AnnullaProgrammato (); resetta la programmazione di una nuova ricerca
Let gnProgrammato = ScheduleFunction (SCORRE_RIGHE, MAX_RICERCA / 100)
; continua il ciclo finché la programmazione non sia interrotta o si superi il tempo massimo
While gnProgrammato && (GetTickCount () - iStart) < MAX_RICERCA
; rimuove dalla stringa memorizzata spazi iniziali e finali, e caratteri di tabulazione
Let sTesto = TagliaSpazi (ToglieTab (gsRiga))
If sTesto Then; se vi è ancora del contenuto nella riga,
Let sElemento = CercaElementi (sTesto, iPartenza); esegue la ricerca degli elementi
If sElemento Then; se un elemento è stato trovato,
AnnullaProgrammato (); resetta la programmazione di una nuova ricerca
SpostaAElemento (sElemento, iPartenza); sposta il cursore all’inizio dell’elemento trovato,
Return; e interrompe il flusso
EndIf; fine controllo esito
EndIf; fine controllo testo
If gnProgrammato Then; se nessun elemento è stato ancora trovato,
If GetCurrentScriptKeyName () == ESC Then; se si è premuto il tasto Escape,
SpeechOn (); riattiva la sintesi
SayMessage (OT_ERROR, msgStopRicerca); avvisa dell’azione,
AnnullaProgrammato (); e resetta la programmazione di una nuova ricerca, interrompendo il ciclo
Else; altrimenti, se la ricerca continua,
If gnVerso == AVANTI Then; se la ricerca avviene verso il basso,
NextLine (); si muove alla riga successiva
Else; Altrimenti,
PriorLine (); torna alla riga precedente
EndIf; fine controllo direzione
Refresh (); resetta la schermata
Let gsRiga = StringLower (RigaCorrente ()); registra la riga sul cursore, portandola in minuscolo
If gsRiga == gsPrecedente Then; se la riga attuale è la stessa della precedente,
Let gnRipeti = gnRipeti + 1; aggiorna il contatore
Else; altrimenti,
Let gsPrecedente = gsRiga; aggiorna la variabile globale
Let gnRipeti = FALSE; e resetta il contatore
EndIf; fine controllo riga precedente
If gnRipeti < MAX_RIGHE Then; se non si è raggiunto il limite massimo delle ripetizioni,
If iPartenza Then; se il valore è impostato,
Let iPartenza = FALSE; lo resetta
EndIf; fine controllo impostazione iniziale
Else; Altrimenti, se si è raggiunta una delle due estremità del documento,
AnnullaProgrammato (); resetta la programmazione di una nuova ricerca
Let iEstremi = TRUE; annota la situazione
EndIf; fine controllo posizione
EndIf; fine controllo prosecuzione ricerca
EndIf; fine controllo ritrovamento e tasti premuti
EndWhile; fine ciclo ricerca
If !gnProgrammato Then; se non è impostato alcun riavvio della funzione,
SpeechOn (); riattiva la sintesi
If iEstremi Then; se è stata raggiunta un’estremità del documento,
If gnVerso == AVANTI Then; se ci si doveva muovere verso il basso, formatta e legge l’avviso
SayFormattedMessage (OT_ERROR, ttlContinuaCerca, StringChopLeft (msgFine, 3), _A, msgFine)
Else; altrimenti, in caso di ricerca verso l’alto, formatta e pronuncia l’avviso
SayFormattedMessage (OT_ERROR, ttlContinuaCerca, StringChopLeft (msgInizio, 2), _O, msgInizio)
EndIf; fine controllo direzione
EndIf; fine controllo estremi documento
SayLine ()
EndIf; fine controllo funzioni
EndFunction
Collaudo.
- Anche se ci si trova alla fine di un corposo blocco, oltre a verificare il buon esito della compilazione, un primo collaudo del nostro codice lo potremo effettuare solo al termine del capitolo.
***
Gestione delle ricerche.
Realizzato lo strumento di ricerca, che ha coinvolto ben 25 funzioni, siamo infine giunti al codice che ci consente di cercare in modo rapido delle stringhe predeterminate, che abbiamo definito anche
"Elementi di ricerca".
Si parte subito con il pezzo principale, che si occupa di gestire ogni tipo di ricerca rapida da noi prevista. Il suo nome è composto dall’azione svolta,
Muove
, e dal classico suffisso
"Dato"
, quest’ultimo già utilizzato più volte sinora.
Essa consente in particolare di cercare quattro tipi di elementi testuali:
- I commenti ad inizio riga, dove il carattere Punto e Virgola è posto sulla prima colonna.
- Singoli elementi di ricerca, richiamati direttamente da combinazioni tasti con suffisso numerico da 1 a 9.
- Elementi di codice dentro all’Editor di Jaws, come stanno già facendo gli script
MuoveElementoSuccessivo ()
e
MuoveElementoPrecedente (). - Gruppi di elementi testuali, di cui avviare la ricerca con gli stessi tasti dei due script appena citati,
Windows+FrecciaGiù
e
Windows+FrecciaSu
, ma che ricercano i soli elementi impostati per l’estensione del documento corrente.
In realtà, per il momento riusciremo a testare solo il primo di questi tipi, realizzando i due script che concluderanno il capitolo, e ci consentiranno finalmente di effettuare un qualche collaudo. L’altra funzione elaborata in questo blocco finale è l’ennesima, e stavolta definitiva, versione di
GestisceDato ()
, la quale si occupa materialmente di chiamare il principale elemento di codice, del quale inizieremo ad occuparci ora.
Esercizio 12.6.1. La funzione MuoveDato.
FileScript.
Default.JSS
Nome.
MuoveDato
Descrizione.
Gestisce i vari tipi di ricerca di elementi nel documento corrente, determinando se utilizzare le funzioni native o se servirsi dell’apposita procedura.
Ritorni.
Di tipo Void. Nessuno.
Parametri.
- sEstensione. L’estensione del documento corrente, che determina i tipi di dati da cercare. Di tipo String.
- sProg. L’eventuale numero progressivo che identifica l’elemento da ricercare. Di tipo String. Parametro Opzionale.
- iAiuto. Se valorizzato con TRUE, attiva l’omonima fase. Di tipo Int. Parametro Opzionale.
Novità.
- Le nostre funzioni
ArchivioDati (),
CercaVoci (),
MisuraMovimento ()
e
ScorreRighe (). - La costante
PUNTO_VIRGOLA
, che contiene l’omonimo carattere. - Le costanti
COMMENTO
e
SINGOLO
, che equivalgono all’omonimo termine.
Fasi.
- La prima via della struttura principale consente di raggiungere un commento nel codice, e consta soltanto nell’impostare il carattere Punto e Virgola come l’elenco degli elementi da cercare, e come conseguenza il valore 1 come numero di tali elementi.
- La seconda via della struttura si occupa invece di trovare i singoli elementi di ricerca; per prima cosa viene riconvertita la categoria, poi si controlla l’esistenza dei file di configurazione, tramite la nostra funzione
ArchivioDati ()
; di seguito, si controlla che una stringa per l’elemento richiesto sia registrata, grazie alla nostra
CercaVoci ()
; se così non è, i tasti premuti per attivare gli script sono ripetuti, passandoli all’applicazione; altrimenti, il flusso continua, limitando anche in questo caso ad 1 gli elementi da ricercare. - La terza via della struttura è, come detto, la stessa funzionalità già coperta dall’attuale forma degli script per lo spostamento agli elementi di codice successivo e precedente; per questo, viene solo riproposto il contenuto di tale script.
- La quarta via della struttura, quella che riguarda la ricerca dei gruppi di elementi, ripropone i controlli inseriti nella seconda, con la differenza che, nel caso non si trovino delle stringhe registrate del tipo indicato, il flusso viene semplicemente interrotto senza ripetere i tasti premuti; se invece gli elementi di ricerca sono stati trovati, ne viene inoltre creato un elenco, casomai ridotto, sulla base delle impostazioni attive.
- Se non si sono verificati errori, il flusso esce dalla struttura principale ed imposta la direzione della ricerca, verso il basso o verso l’alto;
- Poi, la nostra funzione
MisuraMovimento ()
approfitta della necessità di muovere il cursore di un carattere, al fine di evitare riletture durante le ricerche in sequenza, per controllare che Jaws riesca a leggere le righe e le colonne del documento; se anche così non fosse, il flusso comunque continua, e si interrompe solo qualora ci si trovi ad uno degli estremi del documento, pronunciando il messaggio relativo. - Infine, se tutto procede, sono impostati gli ultimi valori necessari al sistema, viene spenta la sintesi e chiamata la nostra funzione
ScorreRighe ()
, che avvia materialmente la procedura di ricerca.
Note.
- Nonostante nell’immediato si riesca a sfruttare solo la prima opzione tra le quattro consentite, così come detto nella premessa, la funzione è comunque già nella sua versione definitiva, predisposta a soddisfare anche gli altri tipi di ricerca previsti.
Codice.
Void Function MuoveDato (string sEstensione, string sProg, int iAiuto)
Var
String sSezione, ; nome della sezione da elaborare
Int iPartenza; colonna da dove è partita la ricerca
If gsCategoria == COMMENTO Then; se si sta cercando un commento ad inizio riga,
Let gsElenco = PUNTO_VIRGOLA; pone il carattere omonimo come unico elemento da cercare
Let gnTotali = 1; Imposta il valore
ElIf gsCategoria == SINGOLO Then; ancora, se il dato da cercare è un singolo elemento di ricerca,
Let gsCategoria = ELEMENTO; imposta la categoria reale
If !ArchivioDati () Then; se il file di configurazione è mancante o incompleto,
Return; interrompe il flusso
EndIf; fine controllo file configurazione
Let sSezione = gsCategoria + sEstensione; compone la sezione dove leggere i dati
Let gsElenco = StringLower (LeggeRecord (gsArchivio, sSezione, sProg)); tenta di rilevare il dato, portandolo casomai in minuscolo
If !gsElenco Then; se il dato non è registrato,
TypeCurrentScriptKey (); ripete i tasti premuti,
Return; e interrompe il flusso
EndIf; fine controllo dati
Let gnTotali = 1; Imposta il valore
ElIf SiamoNellaFinestra (MANAGER) ; se si è nell’Editor di Script,
&& sEstensione == JSS Then; e se si sta elaborando un file script,
If gsOggetto == SUCCESSIVO Then; se la ricerca è è verso il basso,
TypeKey (F2); richiama il relativo comando originale
Else; altrimenti, se la ricerca è verso l’alto,
TypeKey (SHIFT_F2); richiama il relativo comando originale
EndIf; fine controllo direzione
Refresh (); resetta la schermata
SayLine (); legge la riga corrente,
Return; e interrompe il flusso
ElIf gsCategoria == ELEMENTO Then; se invece si deve cercare tra gli elementi registrati per l’estensione corrente,
If !ArchivioDati () Then; se il file di configurazione è mancante o errato,
Return; interrompe il flusso
EndIf; fine controllo archivio dati
Let sSezione = gsCategoria + sEstensione; compone la sezione dove leggere i dati
CercaVoci (sSezione, sEstensione); raccoglie i dati
If !gnTotali Then; se nessuna voce è ancora registrata,
Return; interrompe il flusso
EndIf; fine controllo numero voci
; compone la lista delle voci da cercare, portandole in caratteri minuscoli
Let gsElenco = StringLower (CreaElenco (gsArchivio, sSezione, PRIMA))
Let gnTotali = StringSegmentCount (gsElenco, BELL); aggiorna il numero delle voci valide per la ricerca
EndIf; fine controllo categorie
If gsOggetto == SUCCESSIVO Then; se la ricerca è è verso il basso,
Let gnVerso = AVANTI; imposta il valore corrispondente alla direzione indicata
Else; altrimenti, se si deve cercare verso l’alto,
Let gnVerso = INDIETRO; imposta il valore corrispondente alla direzione indicata
EndIf; fine controllo direzione
If !MisuraMovimento (gnVerso, iPartenza) Then; se il cursore è fermo a fine o inizio documento,
Return; interrompe il flusso
EndIf; fine controllo spostamento
Let gsRiga = RigaCorrente (gnVerso); rileva il testo sulla base del verso impostato
Let gnPrimo = FALSE; azzera il valore per far leggere le istruzioni su come interrompere la ricerca
SpeechOff (); spegne la sintesi
ScorreRighe (iPartenza); chiama l’apposita funzione, specificando i parametri della ricerca iniziale
EndFunction
Collaudo.
- Nell’attesa di poter finalmente provare il nostro lavoro, tra un paio di elementi, ricordatevi di porre come opzionali gli ultimi due parametri di questa funzione.
Esercizio 12.6.2. L’ultimo aggiornamento di GestisceDato.
FileScript.
Default.JSS
Nome.
GestisceDato
Novità.
- Le costanti
ESEGUE,
MUOVE
e
METTE
, che equivalgono tutte all’omonimo termine, le quali stanno ad indicare le azioni compiute dagli script legati alle diverse procedure da approntare.
Note.
- Le modifiche alla precedente versione iniziano col eliminare la riga contenente l’istruzione
"|| sAzione == TORNA Then"
, inserendo al suo posto le successive due righe di codice: - Poi, a partire dalla riga successiva all’istruzione
"CallFunctionByName (sAzione + DATO, sNomeFile, sOggetto, sProg, iAiuto)"
, conviene eliminare tutto il codice fino al comando di fine funzione,
"EndFunction"
, per inserire la restante parte della nuova versione: - Come al solito, è sempre possibile controllare il testo che segue, sostituendo tutto il codice con quello nuovo; anche in questo caso, come nella funzione precedente, la versione proposta sarà comunque quella definitiva.
|| sAzione == TORNA ; o si vuole ritornarvi,
|| sAzione == ESEGUE Then; o, infine, se si vuole attivare uno script a gestione personale,
Else; altrimenti, in tutti i casi in cui è necessario un suffisso,
If sCategoria == SCRIPTS Then; se si stanno elaborando gli script a gestione personale,
Let sSuffisso = sNomeFile; passa il nome del file parametro al posto dell’estensione
ElIf !sSuffisso Then; se un suffisso non è stato rilevato,
If iAiuto Then; se è attiva la fase,
SayMessage (OT_ERROR, hlpNoEst, hlpNoEst_corto); legge l’avviso
EndIf; fine controllo aiuto
Return; in ogni caso, interrompe il flusso
EndIf; fine controllo suffisso
; rende globali i dati rilevati
Let gsCategoria = sCategoria
Let gsOggetto = sOggetto
If sAzione == MUOVE ; se si è chiesto di muoversi ad un elemento,
|| sAzione == METTE Then; oppure, di inserire del testo nel documento corrente,
CallFunctionByName (sAzione + DATO, sSuffisso, sProg, iAiuto); esegue l’azione
ElIf sAzione == ELABORA Then; se invece si è chiesto di avviare una procedura guidata,
If gsCategoria == ELEMENTO Then; se si stanno configurando gli elementi di ricerca,
Let gsOggetto = NULLO; resetta il dato
EndIf; fine controllo oggetto
CallFunctionByName (sAzione + DATO, sSuffisso, iAiuto); esegue l’azione
Else; se invece l’azione non è stata riconosciuta,
If iAiuto Then; se la fase è attiva,
SayFormattedMessage (OT_ERROR, hlpNoAzione, hlpNoAzione_corto, sAzione); legge l’avviso
EndIf; fine controllo Aiuto
EndIf; fine controllo azioni con suffisso
EndIf; fine controllo azioni su documenti
EndIf; fine controllo generale azioni
Codice.
Void Function GestisceDato (string sAzione, string sOggetto)
If !SiamoNellaFinestra (EDITING) Then; se non si è nella finestra corretta,
If !(sAzione + sOggetto) Then; se non sono stati specificati dei parametri,
TypeCurrentScriptKey (); ripete i tasti premuti,
EndIf; fine controllo parametri
Return; interrompe il flusso
EndIf; fine controllo finestra
Var
Int iAiuto, ; stato di attivazione della fase Aiuto
Int iNoVoce, ; imposta la disattivazione della fase Aiuto
String sProg, ; eventuale numero progressivo indicato tramite il nome dello script
String sCategoria, ; tipologia dell’azione da compiere
String sDato, ; dato testuale da sottoporre all’azione
String sNomeFile, ; nome completo del documento aperto
String sSuffisso; dato suppletivo da passare come parametro
Let iAiuto = IsSameScript (); rileva l’eventuale doppia pressione dei tasti di attivazione
If !sAzione Then; se l’azione da compiere non è stata specificata come parametro,
Let sProg = SeparaParole (sAzione, sCategoria, sOggetto); estrae i dati dal nome script
Else; altrimenti
Let iNoVoce = SALTA; imposta la successiva disattivazione della fase Aiuto
EndIf; fine controllo parametro
If sAzione == PRONUNCIA Then; se si sono chieste azioni di lettura,
If sCategoria == STATO Then; se si sono richiesti dati dalla barra di stato,
Let sDato = InfoStato (sOggetto, iAiuto); rileva il dato dalla funzione generica
Else; altrimenti, in tutti gli altri casi,
; Rileva il dato dalla funzione di cui si compone il nome
Let sDato = NoZero (CallFunctionByName (sCategoria + sOggetto))
EndIf; fine controllo categoria
CallFunctionByName (sAzione + DATO, sOggetto, sDato, iAiuto); esegue l’azione
ElIf sAzione == CERCA Then; se invece si sono chieste delle ricerche,
CercaDato (sCategoria, sOggetto, iAiuto); esegue l’azione
Else; se invece servono operazioni su documenti,
If !DocumentoCorrente (sNomeFile, sSuffisso) Then; se non vi sono dati sul file aperto,
If iAiuto Then; se è attiva la fase Aiuto,
SayFormattedMessage (OT_ERROR, hlpNoDato, hlpNoDato_corto, msgTitolo); legge l’avviso
EndIf; fine controllo aiuto
Return; in ogni caso, interrompe il flusso
EndIf; fine controllo documento
If sAzione == SALVA; se si vuole il salvataggio di un contrassegno,
|| sAzione == TORNA ; o si vuole ritornarvi,
|| sAzione == ESEGUE Then; o, infine, se si vuole attivare uno script a gestione personale,
If !iNoVoce Then; se l’impostazione non è disattivata,
Let iAiuto = TRUE; imposta la fase
EndIf; fine controllo disattivazione
CallFunctionByName (sAzione + DATO, sNomeFile, sOggetto, sProg, iAiuto); esegue l’azione
Else; altrimenti, in tutti i casi in cui è necessario un suffisso,
If sCategoria == SCRIPTS Then; se si stanno elaborando gli script a gestione personale,
Let sSuffisso = sNomeFile; passa il nome del file parametro al posto dell’estensione
ElIf !sSuffisso Then; se un suffisso non è stato rilevato,
If iAiuto Then; se è attiva la fase,
SayMessage (OT_ERROR, hlpNoEst, hlpNoEst_corto); legge l’avviso
EndIf; fine controllo aiuto
Return; in ogni caso, interrompe il flusso
EndIf; fine controllo suffisso
; rende globali i dati rilevati
Let gsCategoria = sCategoria
Let gsOggetto = sOggetto
If sAzione == MUOVE ; se si è chiesto di muoversi ad un elemento,
|| sAzione == METTE Then; oppure, di inserire del testo nel documento corrente,
CallFunctionByName (sAzione + DATO, sSuffisso, sProg, iAiuto); esegue l’azione
ElIf sAzione == ELABORA Then; se invece si è chiesto di avviare una procedura guidata,
If gsCategoria == ELEMENTO Then; se si stanno configurando gli elementi di ricerca,
Let gsOggetto = NULLO; resetta il dato
EndIf; fine controllo oggetto
CallFunctionByName (sAzione + DATO, sSuffisso, iAiuto); esegue l’azione
Else; se invece l’azione non è stata riconosciuta,
If iAiuto Then; se la fase è attiva,
SayFormattedMessage (OT_ERROR, hlpNoAzione, hlpNoAzione_corto, sAzione); legge l’avviso
EndIf; fine controllo Aiuto
EndIf; fine controllo azioni con suffisso
EndIf; fine controllo azioni su documenti
EndIf; fine controllo generale azioni
EndFunction
Esercizio 12.6.3. Lo script MuoveCommentoPrecedente.
FileScript.
Default.JSS
Nome.
MuoveCommentoPrecedente
Sommario.
Si muove alla riga di commento precedente.
Descrizione.
Cerca verso l’inizio del documento corrente la prima riga che inizia con il carattere Punto e Virgola, spostandovi se la trova.
TastiAttivazione.
Shift+Windows+UpArrow
Codice.
Script MuoveCommentoPrecedente ()
GestisceDato ()
EndScript
Collaudo.
- Giunti al primo script, una volta compilato correttamente, potremo già ora iniziare a raccogliere i frutti del nostro lavoro, ma chiediamo un altro briciolo di pazienza, quella che serve a realizzare anche il prossimo ed ultimo elemento del capitolo.
Esercizio 12.6.4. Lo script MuoveCommentoSuccessivo.
FileScript.
Default.JSS
Nome.
MuoveCommentoSuccessivo
Sommario.
Si muove alla riga di commento successiva.
Descrizione.
Cerca verso la fine del documento corrente la prima riga che inizia con il carattere Punto e Virgola, spostandovi se la trova.
TastiAttivazione.
Shift+Windows+DownArrow
Codice.
Script MuoveCommentoSuccessivo ()
GestisceDato ()
EndScript
Collaudo.
- Compilato lo script, portatevi all’inizio di una funzione di un certo volume, come le ultime
GestisceDato ()
o
MetteDato (). - Premete i tasti di attivazione per la ricerca dei commenti verso il basso,
Shift+Windows+FrecciaGiù
, e il cursore dovrebbe posizionarsi nella prima riga che inizia con il carattere Punto e Virgola. - Premete ancora una volta la stessa combinazione, per portarvi sulla seconda riga di commento, quindi provate a tornare al commento precedente, premendo
Shift+Windows+FrecciaSu
; dovreste tornare alla prima riga incontrata. - Questo sistema di ricerca rapida è utile, in particolare, quando in fase di messa a punto degli script, magari per fare una prova, si pone un carattere Punto e Virgola per impedire l’esecuzione di una particolare istruzione; poi, una volta concluso le modifiche, se gli script realizzati funzionano lo stesso, si potrà ripassare velocemente il codice eliminando in modo definitivo tutte le istruzioni che avevamo nascosto dietro un carattere di commento.
***
Riepilogo.
La sensazione potrebbe essere quella di una montagna che abbia partorito un topolino, data la mole del lavoro svolto nel capitolo e le poche applicazioni che siamo riusciti a concretizzare. Più di così, tuttavia, non si poteva proprio fare, almeno senza aver prodotto il codice che manca, ovvero quello che ci consente di avviare delle procedure guidate per l’inserimento delle stringhe testuali da cercare o inserire nei documenti.
Nel dettaglio, la ricerca del commento ad inizio riga è l’unica che possiamo realizzare in questo momento perché, appunto, è l’unico caso in cui l’elemento da ricercare non è solo
"predeterminato"
, cioè stabilito a priori, bensì è
"Predefinito"
, cioè già stabilito in modo definitivo ed invariabile nel carattere Punto e Virgola.
Per questo, l’obiettivo dei prossimi capitoli sarà quello di realizzare le procedure guidate che ci consentano di specificare, modificare o cancellare le stringhe da registrare, per poter poi essere utilizzate per la loro ricerca o il loro inserimento nei nostri documenti.
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: