Numero 09 del 2021
Titolo: Gli script di Jaws 13b. Le procedure guidate: controllo dei dati e funzioni hook.
Autore: Abramo Volpato.
Articolo:
Gli script di Jaws 13b. Le procedure guidate: controllo dei dati e funzioni hook.
Di Abramo Volpato.
Esercizio 13.2.4. La funzione NumeroDati.
FileScript. Default.JSS
Nome. NumeroDati
Descrizione. Controlla se il numero dei dati presenti nel record, relativo ai parametri specificati, equivale a quello registrato nella variabile globale. In ogni caso, trasmette per riferimento il record di dati rilevato.
Ritorni. Di tipo Int. True, per un numero corretto di dati; FALSE, per un numero errato.
Parametri.
1. sChiave. La chiave del record da leggere. Di tipo String.
2. sRecord. Per Riferimento. I dati presenti nel record, trasmessi a prescindere dal numero dei campi rilevati. Di tipo String. Parametro Opzionale.
Note.
1. Il controllo svolto da tale funzione è molto importante, poiché nell'archivio non possono esserci record con dati incompleti; come analizzeremo più avanti, nel caso di esito negativo di questo controllo, sarà data la possibilità di cancellarli, oppure di completarne il numero.
Codice.
Int Function NumeroDati (string sChiave, string ByRef sRecord)
Let sRecord = LeggeRecord (gsArchivio, gsSezione, sChiave); rileva il record di dati
; se il numero dei campi del record coincide con quelli rilevati nell'archivio,
If StringSegmentCount (sRecord, PIPE) == gnMaxCampi Then
Return TRUE; restituisce l'esito positivo
Else; altrimenti,
Return FALSE; restituisce un risultato nullo
EndIf; fine controllo numero dati
EndFunction
Collaudo.
1. Dopo aver compilato, il secondo parametro, quello trasmesso per riferimento, deve essere reso opzionale seguendo la solita procedura, in quanto non sarà sempre indispensabile all'elemento di codice chiamante.
2. Se volete, potete ottimizzare i tempi della modifica richiesta attendendo le successive due funzioni per realizzarla, in quanto anch'esse saranno dotate di parametri opzionali.
Esercizio 13.2.5. La funzione CampoTrovato.
FileScript. Default.JSS
Nome. CampoTrovato
Descrizione. Cerca, e casomai trasmette per riferimento, la posizione del campo indicato all'interno del record di dati specificato.
Ritorni. Di tipo Int. TRUE, per un campo trovato; FALSE, per nessun ritrovamento.
Parametri.
1. sElenco. Il record con gli elementi tra i quali cercare. Di tipo String.
2. sTesto. Il contenuto del campo da cercare. Di tipo String.
3. iPosiz. Per Riferimento. Il numero del campo individuato. Di tipo Int. Parametro Opzionale.
4. sSeparatore. Per Riferimento. L'eventuale carattere di separazione da impostare; nel caso in cui non si indichi nulla, sarà usato, e ritrasmesso, il carattere Pipe, Ascii 124. Di tipo String. Parametro Opzionale.
Note.
1. Questa funzione è utile nelle occasioni in cui è necessario controllare se un dato sia presente in un elenco, o tra i campi di un record; il vantaggio, rispetto al solo utilizzo della funzione integrata "StringSegmentIndex ()", che svolge proprio tale compito, è che la nostra può anche restituire nel contempo la posizione del dato richiesto nell'ambito in cui cercare.
2. La funzione, inoltre, imposta, e ritrasmette per riferimento, un carattere Pipe come separatore, qualora non ne sia già stato indicato uno come quarto parametro.
Codice.
Int Function CampoTrovato (string sRecord, string sCampo, ; primi due parametri,
int ByRef iPosiz, string ByRef sSeparatore); seconda riga con gli altri due
ImpostaPipe (sSeparatore); se non lo si specifica, imposta come separatore il carattere Pipe
; cerca il contenuto del campo richiesto
Let iPosiz = StringSegmentIndex (StringLower (sRecord), sSeparatore, StringLower (sCampo), TRUE)
If iPosiz Then; se un campo è stato trovato,
Return TRUE; restituisce l'esito positivo
Else; altrimenti,
Return FALSE; restituisce un risultato nullo
EndIf; fine controllo esito ricerca
EndFunction
Collaudo.
1. Dopo aver completato correttamente la compilazione, anche qui ci sono dei parametri da rendere opzionali, gli ultimi due; ricordiamo, tuttavia, che potete attendere di realizzare anche la prossima funzione , per completare il trittico di nostri elementi per i quali si deve aggiornare il file di documentazione predefinito.
Esercizio 13.2.6. La funzione AggiornaTasti.
FileScript. Default.JSS
Nome. AggiornaTasti
Descrizione. Scrive, aggiorna o cancella le assegnazioni dei tasti abbinate agli script a gestione personale, nel file tasti dell'applicazione corrente, sulla base del tipo specificato come terzo parametro.
Ritorni. Di tipo Int. Il numero delle modifiche effettuate.
Parametri.
1. iTipo. L'eventuale tipo di azione da compiere; i valori possibili sono: 0, o FALSE, per aggiornare i dati modificati durante la sessione corrente; 1, o la costante CANCELLA, per rimuovere i dati relativi alla chiave specificata come terzo parametro o registrata nella variabile globale gsChiave; 2, o la costante TUTTI, per controllare la corretta composizione delle assegnazioni relative a tutti gli script a gestione personale per l'applicazione corrente, aggiornando gli script registrati ed eliminando quelli senza riferimenti validi. Di tipo Int. Parametro Opzionale.
2. sChiave. L'eventuale chiave relativa allo script da rimuovere; qualora non sia indicata, in caso di cancellazione sarà usato il valore impostato nella variabile globale gsChiave. Di tipo String. Parametro Opzionale.
Novità.
1. Le nostre funzioni "NumeroDati ()" e "CampoTrovato", ().
2. Le costanti "CANCELLA" e "TUTTI", che come detto hanno valore, nell'ordine, 1 e 2, e servono per indicare il tipo di azione da compiere sui tasti di attivazione degli script.
Fasi.
1. Una prima struttura di controllo compone l'elenco delle chiavi dei record da elaborare, sulla base del tipo indicato come primo parametro; qualora si sia specificato di cancellare un'assegnazione tasti, l'eventuale secondo parametro viene usato come chiave del record da eliminare.
2. Una seconda breve struttura controlla che i dati non siano insufficienti per proseguire, e casomai interrompe il flusso.
3. Un'ulteriore struttura si occupa di individuare sia le combinazioni tasti per attivare gli script, sia i relativi nomi reali, partendo dalle chiavi rilevate, utilizzando la nostra funzione "NumeroDati ()".
4. Un ciclo generale scorre poi l'elenco predefinito delle sezioni nei file tasti; all'interno di ciascuna, sulla base dell'azione di scrittura o di rimozione delle assegnazioni, viene comunque controllato, prima della modifica, che a ciascuna assegnazione corrisponda l'esatto nome di script reale; se viene trovata un'assegnazione che coinvolge un nome di script personale che non esiste, essa viene cancellata per rendere disponibili tali tasti per le associazioni con altri script.
5. Solo nel caso che il controllo sia esteso a tutti gli script, è eseguito dapprima un secondo ciclo per verificare la presenza di precedenti assegnazioni relative ai tasti da aggiornare, servendosi anche della nostra funzione "CampoTrovato ()"; qualora tale ricognizione non completi l'elenco degli aggiornamenti da effettuare, un ulteriore ciclo secondario trascrive le rimanenti coppie di tasti e script a gestione personale.
6. Al termine delle elaborazioni, se il numero delle modifiche coincide con quelle da effettuare, viene espresso l'esito positivo restituendo il numero delle modifiche stesse; se invece non tutte, o nessuna, delle modifiche sono riuscite, viene restituito un risultato nullo.
Note.
1. Così come si accennava nella premessa, e come si può intuire dal numero delle fasi, questa funzione è uno dei pezzi principali della procedura; essa consente infatti di gestire ogni esigenza legata alla gestione dei file tasti, dall'aggiornamento, al controllo, ed alla verifica di eventuali impostazioni errate.
2. La grande attenzione prestata ai controlli, in particolare, è dovuta in questo caso al fatto che l'esatta correlazione, tra gli script a gestione personale e le combinazioni tasti che li attivano, è gestita totalmente da noi; per altri aspetti degli script di tale tipo, si possono invece sfruttare a nostro supporto, come analizzeremo più avanti, anche delle interessanti funzioni integrate di Jaws.
Codice.
Int Function AggiornaTasti (int iTipo, string sChiave)
Var
String sElenco, ; elenco delle chiavi rilevate
Int i, ; contatore di un primo ciclo
String sSezTasti, ; sezione del file tasti
Int j, ; contatore del ciclo generale per le sezioni
Int k, ; contatore del ciclo secondario per le chiavi
Int l, ; contatore del ciclo interno sugli script
String sRecord, ; Dati dello script
String sTasti, ; tasti di attivazione degli script personali
String sNomi, ; nomi reali degli script
String sChiavi, ; elenco delle combinazioni tasti
String sTasto, ; singola combinazione di tasti
String sNome, ; nome dello script
String sDato, ; eventuale nome precedente
Int iPosiz, ; posizione del campo nel record
Int iCambiare, ; numero degli script da aggiornare
Int iModificati, ; script effettivamente cambiati
Int iScript, ; numero degli script personali rilevati
String sElaborati; script personali già trattati
If iTipo == TUTTI Then; se si devono controllare le combinazioni tasti di tutti gli script,
Let sElenco = LeggeChiavi (CORRENTE, gsSezione); rileva le chiavi dall'archivio
ElIf iTipo == CANCELLA Then; se invece ci sono da rimuovere i tasti dello script corrente,
If sChiave Then; se si è specificata una chiave come parametro,
Let sElenco = sChiave; la imposta
Else; altrimenti,
Let sElenco = gsChiave; imposta la chiave attiva
EndIf; fine controllo chiave
Else; altrimenti, se si devono aggiornare gli script modificati durante la sessione,
Let sElenco = gsNuoviTasti; imposta il contenuto dell'apposita variante globale
EndIf; fine controllo elenco chiavi
If !sElenco ; se nessuna chiave è stata rilevata,
&& iTipo != TUTTI Then; e non si è prevista un'analisi globale del file tasti,
Return; interrompe il flusso
EndIf; fine controllo chiavi
If iTipo == CANCELLA Then; se si deve soltanto rimuovere una voce,
Let sTasti = gsRimuoviTasti;imposta la combinazione salvata
; e compone anche il relativo nome reale di script
Let sNomi = FormatString (msgPersonalScript, sElenco)
Else; altrimenti, negli altri casi,
For i = 1 To StringSegmentCount (sElenco, PIPE); scorre le chiavi relative agli script personali
Let sChiave = StringSegment (sElenco, PIPE, i); estrae la singola chiave
If NumeroDati (sChiave, sRecord) Then; se i dati sono presenti nel numero corretto,
; aggiorna gli elenchi, aggiungendo la nuova voce in coda, relativi a:
Let sTasti = CambiaCampo (sTasti, StringSegment (sRecord, PIPE, SECONDA), ULTIMA); tasti
Let sNomi = CambiaCampo (sNomi, FormatString (msgPersonalScript, sChiave), ULTIMA); nomi reali
EndIf; fine controllo numero dati
EndFor; fine scansione chiavi
EndIf; fine controllo tipo aggiornamento
Let iScript = StringSegmentCount (sTasti, PIPE); conta gli eventuali script personali rilevati
; scorre le sezioni predefinite dei file tasti
For j = 1 To StringSegmentCount (lstSezioniTasti, PIPE)
Let sSezTasti = StringSegment (lstSezioniTasti, PIPE, j); estrae la singola sezione da elaborare
If iTipo == CANCELLA Then; se si deve solo cancellare una combinazione tasti,
Let sDato = LeggeDato (APP, sSezTasti, sTasti); rileva il dato dal file tasti corrente
If sDato ; se un nome script abbinato è stato rilevato,
&& sDato == sNomi Then; e coincide con il nome da cancellare,
If CancellaChiave (APP, sSezTasti, sTasti) Then; se la rimozione riesce,
Return TRUE; restituisce l'esito positivo
Else; altrimenti,
Return FALSE; restituisce un risultato nullo
EndIf; fine controllo cancellazione
EndIf; fine controllo dato
Else; altrimenti, se si devono comunque controllare tutte le combinazioni tasti,
Let sChiavi = LeggeChiavi (APP, sSezTasti); rileva l'elenco delle combinazioni tasti
Let sElaborati = NULLO; resetta il dato
For k = 1 To StringSegmentCount (sChiavi, PIPE); scorre le combinazioni di tasti
Let sTasto = StringSegment (sChiavi, PIPE, k); estrae la singola chiave delle assegnazioni
Let sDato = LeggeDato (APP, sSezTasti, sTasto); rileva il nome dello script abbinato
; se la combinazione è tra quelle degli script personali,
If CampoTrovato (sTasti, sTasto, iPosiz) Then
Let sElaborati = CambiaCampo (sElaborati, IntToString (iPosiz), ULTIMA); la aggiunge ad un elenco
Let sNome = StringSegment (sNomi, PIPE, iPosiz); estrae anche il nome reale dello script personale
If sNome != sDato Then; se il nome abbinato è diverso da quello da scrivere,
Let iCambiare = iCambiare + 1; aggiorna il contatore dei dati da aggiornare
If ScriveDato (APP, sSezTasti, sTasto, sNome) Then; se la nuova combinazione è salvata,
Let iModificati = iModificati + 1; aggiorna il contatore dei dati cambiati
EndIf; fine controllo scrittura
EndIf; fine controllo nome
ElIf iTipo == TUTTI Then; se invece l'analisi è estesa a tutti gli script,
If ScriptPersonale (sDato) Then; se si tratta di uno script personale,quindi non valido,
If sTasto Then; se un tasto di cui eliminare l'assegnazione è impostato,
CancellaChiave (APP, sSezTasti, sTasto); rimuove l'assegnazione
EndIf; fine controllo presenza chiave
EndIf; fine controllo validità nome
EndIf; fine controllo combinazioni tasti
EndFor; fine scansione chiavi
If StringSegmentCount (sElaborati, PIPE) < iScript Then; se non tutti gli script sono già presenti,
For l = 1 To iScript; scorre i dati degli script personali
If !CampoTrovato (sElaborati, IntToString (l)) Then; se lo script non è già stato trattato,
Let iCambiare = iCambiare + 1; aggiorna il contatore dei dati da aggiornare
Let sTasto = StringSegment (sTasti, PIPE, l); estrae la chiave con i tasti
Let sNome = StringSegment (sNomi, PIPE, l); estrae anche il nome reale dello script
If ScriveDato (APP, sSezTasti, sTasto, sNome) Then; se la nuova combinazione è salvata,
Let iModificati = iModificati + 1; aggiorna il contatore dei dati cambiati
EndIf; fine controllo scrittura
EndIf; fine controllo presenza elaborati
EndFor; fine scansione script
EndIf; fine controllo numero elaborati
EndIf; fine controllo tipo azione
EndFor; fine scansione sezioni
If iCambiare == iModificati Then; se il numero delle modifiche coincide con quelle da effettuare,
Return iModificati; restituisce il numero dei tasti di attivazione aggiornati
Else; altrimenti,
Return FALSE; restituisce un risultato nullo
EndIf; fine controllo esito aggiornamento
EndFunction
Collaudo.
1. Se avete atteso fino ad ora per rendere opzionali i parametri di questa e delle due funzioni precedenti, allora, dopo aver correttamente compilato, aprite il file di documentazione predefinito, "Default.JSD", cercate la scheda relativa alla prima funzione del gruppo, "NumeroDati", per rendere opzionale il secondo dei due parametri presenti tra quelli elencati.
2. Sempre proseguendo nelle modifiche cumulative, passate alla scheda, "CampoTrovato", dove la parola chiave è da inserire tra il secondo ed il terzo parametro.
3. Infine, ed in ogni caso, arrivando alla scheda della presente funzione, i due parametri sono entrambi da porre come opzionali.
4. Soltanto ora, quindi, si possono salvare le modifiche, compilare nuovamente ed uscire dall'Editor di Script, per rendere effettivi i cambiamenti.
Esercizio 13.2.7. La funzione InizializzaProcedura.
FileScript. Default.JSS
Nome. InizializzaProcedura
Descrizione. Esegue i settaggi iniziali, salvando anche i parametri della procedura nelle apposite variabili globali.
Ritorni. Di tipo Int. True, per settaggi regolari; FALSE, per dati incompleti.
Parametri.
1. sSuffisso. L'estensione, o il nome file, del documento corrente. Di tipo String.
Novità.
1. Le nostre funzioni "RilevaCampi ()" e "AggiornaTasti ()".
Fasi.
1. L'intera funzione è un'unica struttura di controllo, dove la prima via controlla i passaggi nei quali non si devono effettuare le impostazioni iniziali; all'interno di questa opzione, nel primo caso si incrementa il numero del campo trattato, nell'altro ci si limita ad annullare il valore che rende possibile il salto delle citate impostazioni.
2. La seconda via è quella che viene invece intrapresa nei normali passaggi, quando vi sono da effettuare tutte le impostazioni ed i controlli preliminari; qui si inizia con un breve controllo, effettuato tramite la nostra funzione "RilevaCampi ()", sulla validità dei dati presenti per la procedura, interrompendo il flusso nel caso di esito negativo.
3. Sempre per la seconda via, qualora il flusso continui, viene impostato il nome globale della sezione dell'archivio dati, sulla base della categoria attiva; durante questa fase viene utilizzata la nostra funzione "AggiornaTasti ()", per controllare l'impostazione preliminare dei file script a gestione personale.
4. In ogni caso, come scelta predefinita, si inizia impostando il valore che fa iniziare dalla schermata dove si elabora il primo campo di ogni record.
Note.
1. Questa funzione è soltanto la prima parte della funzione base della procedura, che per semplicità è stata ridotta di dimensioni e, appunto, spezzettata nel maggior numero di funzioni possibili per mantenerne intatta l'efficacia.
2. Come al solito, ciò è reso possibile dal massiccio ricorso alle variabili globali, che anche in questo caso sono utilizzate sia per dati da elaborare al suo interno, sia per impostazioni da trasmettere agli altri elementi della procedura.
Codice.
Int Function InizializzaProcedura (string sSuffisso)
If gnSalta Then; se si è dopo la procedura di attesa tasti,
If gnFerma Then; se dei tasti sono stati specificati,
Let gnCampo = gnCampo + 1; consente di passare alla schermata successiva
Let gnFerma = FALSE; annulla il valore
Else; altrimenti,
Let gnSalta = FALSE; annulla il valore per il salto della schermata di scelta
EndIf; fine controllo aggiornamenti
Else; altrimenti, in tutti gli altri casi, inizializza il lavoro
If !RilevaCampi () Then; se il rilevamento dei campi ha generato un errore,
Return FALSE; restituisce un risultato nullo
EndIf; fine controllo campi validi
Let gsSuffisso = sSuffisso; rende globale anche il suffisso specificato
Let gnCampo = PRIMA; imposta il valore per la schermata iniziale
If gsCategoria == SCRIPTS Then; se si stanno elaborando gli script a gestione personale,
Let gsSezione = gsCategoria + gsOggetto; compone la sezione nell'archivio dati
AggiornaTasti (TUTTI); Controlla che le assegnazioni degli script personali siano corrette
Else; altrimenti, in tutti gli altri casi, la compone aggiungendovi l'estensione
Let gsSezione = gsCategoria + gsOggetto + gsSuffisso
EndIf; fine controllo categoria
EndIf; fine controllo impostazioni
Return TRUE; restituisce l'esito positivo
EndFunction
13.3. Funzioni hook .
Le funzioni "hook" sono un particolare elemento di codice che interviene solo quando è attivato, sospendendo il normale funzionamento dei tasti premuti. Grazie a questi speciali elementi sarà possibile riconfigurare ogni effetto delle nostre pressioni sulla tastiera, dando a ciascuna di esse un altro significato rispetto a quello originale.
Per fare un esempio, la modalità Aiuto tastiera, che si attiva premendo "Insert+1", è il frutto di una funzione hook. Così, premendo la citata combinazione di tasti, la funzione hook può entrare in azione quando sono premuti dei tasti, ripetendo direttamente il loro nome o, soprattutto, indicando se quegli stessi tasti possono in condizioni normali attivare uno script.
Nel nostro caso, le funzioni hook ci servono per conoscere se sia possibile inserire i tasti di attivazione che noi vorremmo assegnare a degli script a gestione personale. Nel dettaglio, l'inserimento dei tasti avverrà in modo tradizionale, grazie ad una modifica che apporteremo ad una funzione evento che conosciamo già, mentre le funzioni hook servono per sospendere i normali effetti dei tasti e per dirci, appunto, quali tra essi sono già occupati da degli script.
13.3.1. Accendere e spegnere una funzione hook.
In particolare, le funzioni di Jaws che noi andremo a sfruttare per gestire la modalità hook sono le seguenti:
1. "AddHook", (AggiungiHook).
2. "RemoveHook", (CancellaHook).
Come si capisce dalle traduzioni, la prima serve per attivare la modalità che modifica gli effetti della pressione dei tasti, la seconda per cancellarla, ripristinando il normale funzionamento.
Entrambe le funzioni hanno due parametri, che sono uguali tra loro:
1. un valore che identifica il tipo di modalità hook da attivare, che nel nostro caso è lo 0, o la costante nativa "HK_SCRIPT".
2. Il nome della funzione hook cui reindirizzare la gestione dei tasti, che va espresso da solo, senza le parentesi, così come abbiamo già fatto con la funzione "ScheduleFunction ()".
Quindi, mentre le due citate funzioni servono ad accendere e spegnere la modalità hook, l'elemento di codice di cui inseriamo il nome nelle relative istruzioni è quello a cui Jaws reindirizza il controllo dei tasti ai quali è abbinato un qualche script. A questo elemento, che sarà una funzione, potremo assegnare il nome che vogliamo, ed al suo interno andranno posti dei controlli che possano eseguire delle azioni concrete.
A tale funzione, in modo analogo a quello che succede con le funzioni evento, potremo assegnare uno o due parametri, purché di tipo "string", e con un nome a piacere. Essi, tuttavia, avranno già un loro contenuto predefinito: nel nostro caso, sfrutteremo solo il primo, che corrisponde al nome di un eventuale script legato ai tasti premuti.
A questo punto è davvero necessario un esempio per capirci meglio, in cui proporremo una coppia di elementi che avvieranno la modalità speciale, e poi torneranno alla normalità premendo il tasto "Home". Analizzate lo script, che abbiamo chiamato "ModoAlternativo",e la relativa funzione "NuovoEffetto", la quale, una volta attivata la nuova modalità, sarà chiamata a gestire la pressione dei tasti che attivano gli script.
Script ModoAlternativo ()
SayString ("Premere il tasto Home per tornare come prima"); avvisa sul modo di uscirne,
AddHook (HK_SCRIPT, "NuovoEffetto"); ed avvia la modalità speciale
EndScript
Void Function NuovoEffetto (string sNome)
If sNome == "JAWSHome" Then; se si preme il tasto Home,
SayString ("Ecco tornato tutto come prima!"); legge l'avviso,
RemoveHook (HK_SCRIPT, "NuovoEffetto"); interrompe la modalità speciale,
Return TRUE; e restituisce un valore positivo per confermare l'interruzione
Else; altrimenti, in tutti gli altri casi,
SayString (sNome); legge il nome dello script collegato ai tasti premuti
EndIf; fine controllo nome script
EndFunction
In questo caso, gli esempi proposti hanno il seguente funzionamento:
1. Lo script "ModoAlternativo" ha solo la funzione di attivare la nuova modalità di risposta alla pressione dei tasti legati a degli script, tramite la funzione nativa "AddHook ()". A quest'ultima, si deve specificare come secondo parametro il nome della funzione da attivare, che nel nostro caso abbiamo chiamato "NuovoEffetto".
2. Attivato tale script, tramite la pressione dei tasti ad esso associati, la pressione di qualsiasi altro tasto, o combinazione di essi, che sia legato ad un qualche script, passa per la nostra funzione "NuovoEffetto ()"; qui, se lo script collegato ai tasti non viene riconosciuto, il flusso prende la seconda via della struttura di controllo che la compone, limitandosi a pronunciare il nome in inglese dello script.
3. Quando invece si preme come consigliato il tasto "Home", ad esso è collegato lo script nativo "JAWSHome ()", il cui nome viene invece riconosciuto dalla prima via del controllo, che intercetta il flusso e, dopo aver pronunciato un avviso, ripristina la situazione precedente tramite la chiamata della funzione nativa "RemoveHook ()"; da quel momento, la modalità ha termine, e tutto torna come era prima.
Se volete, potete provare anche direttamente l'effetto di questi due elementi di codice, semplicemente copiandoli dentro ad un file script, magari quello del Blocco Note, per non rischiare malfunzionamenti irreversibili, ed assegnando allo script una combinazione tasti a vostra scelta.
***
Per ulteriori spiegazioni, scrivere a:
Abramo Volpato, oppure a: Nunziante Esposito