Sviluppo4d.it
Sito indipendente di informazioni tecniche per sviluppatori 4th Dimension italiani  

Sviluppatori 4D

Utility 4D

Risorse 4D



4d logo
Naviga:

Faq

Ho trovato 823 faq.

Categoria Argomento Commenti
Info Info JSON e i caratteri da filtrare
Nelle stringhe JSON i caratteri da filtrare sono:

virgolette (quote) -> \"
barra a destra (slash) -> \/
barra a sinistra (backslash) -> \\
backspace -> \b
formfeed -> \f
nuova linea (new line, char(10) ) -> \n
a capo (return, char(13) ) -> \r
tabulatore (tab, char(9) ) -> \t
tutti i caratteri da 0 a 31 esclusi quelli sopra devono diventare \x00-\x1F
un qualsiasi carattere unicode -> \u+4 cifre esadecimale


// web_jsonEncode

$risultato:=$1

$risultato:=Replace string($risultato;"\\";"\\\\")
$risultato:=Replace string($risultato;char(double quote);"\\\"")
$risultato:=Replace string($risultato;"/";"\\/")
$risultato:=Replace string($risultato;Char(Backspace);"\\b")
$risultato:=Replace string($risultato;Char(FF ASCII code);"\\f")
$risultato:=Replace string($risultato;Char(Line feed);"\\n")
$risultato:=Replace string($risultato;Char(Carriage return);"\\r")
$risultato:=Replace string($risultato;Char(Tab);"\\t")

$0:=$risultato
1
Tecniche Tecniche [v14] Campi OBJECT e coppie chiavi-valore
E' un classico registrare in coppie di array impostazioni o valori da tenere in memoria.

Tipicamente sono due array, si cerca nel primo e se si trova si prende il valore dal secondo, così:

// PER REEGISTRARE
ARRAY TEXT(preferenzeChiave;3)
ARRAY TEXT(preferenzeValore;3)
preferenzeChiave{1}:="Valuta"
preferenzeValore{1}:="Euro"
preferenzeChiave{2}:="Iva"
preferenzeValore{2}:="22"
preferenzeChiave{3}:="Spese"
preferenzeValore{4}:="2,00"

// PER RIPRENDERE
$ce=Find in array(preferenzeChiave;"Iva")
If ($ce>0)
  $0:= preferenzeValore{$ce}
Else
  $0:=""
End if

Con il nuovo Tipo C_OBJECT le due cose si possono scrivere così:

// PER REEGISTRARE
C_OBJECT(oPreferenze)
OB SET(oPreferenze;"Valuta";"Euro";"Iva";22;"Spese";2.00)

// PER RIPRENDERE
$iva:=OB GET(oPreferenze;"Iva")
Info Info [v15] Campi Object visualizzati come JSON
Nelle form impostando un oggetto di Variabile con il contenuto di un Object si ottiene:
1- la visualizzazione del Json corrispondente (senza dover usare JSON stringify)
2- il Json può essere modificato dall'utente e la variabile si aggiorna!
Info Info [v14] JSON e i campi Time
Quando si usa JSON Stringify o Selection to JSON i campi time vengono trasformati in un numero intero di millisecondi dalla mezzanotte.

Quindi le 10 del mattino diventano 10*3600*1000 = 36 000 000

Per recuperare l'orario originale si possono fare due cose:


$millisec:=36000000

Se mi serve una Stringa:
$testo:=Time String($millisec/1000)
-> $testo = "10:00:00"

Se mi serve un Time:
$ora:=Time($millisec/1000)
-> $ora = ?10:00:00?
Comandi Comandi [v14] I comandi nativi JSON: Selection to JSON
Il comando Selection to JSON crea una stringa JSON con i campi della selezione corrente, ad esempio:

$jsonString :=Selection to JSON([Anagrafica])

Il risultato sarà del tipo :
[
{"cognome":"Rossi", "nome":"Mario"},
{"cognome":"Verdi", "nome":"Giuseppe"},
...
]

E' possibile caricare solo alcuni campi aggiungendoli alla sintassi:

$jsonString :=Selection to JSON([Anagrafica], [Anagrafica]Cognome)
[
{"Cognome":"Rossi"},
{"Cognome":"Verdi"},
...
]


Oppure si può impostare un "template"

  C_OBJECT($template)
  OB SET($template;"LastName";->[Anagrafica] Cognome)
  $jsonString :=Selection to JSON([Anagrafica];$template)

[
{"LastName":"Rossi"},
{"LastName":"Verdi"},
...
]
Comandi Comandi [v14] I comandi nativi JSON: JSON Stringify
Partendo da una variabile C_Object si ottiene l'equivalente in stringa JSON con il comando

JSON Stringify ( value ; * ) -> ritorna una stringa compatta
JSON Stringify ( value ; * ) -> modalità 'pretty print', ritorna una stringa indentata

Esempio:

C_OBJECT($Anagrafica;$Figli)
  OB SET($Anagrafica;"cognome";"Rossi";"nome";"Mario")
  OB SET($Figli;"nome";"Giovanni";"anni";"12")
  OB SET($Anagrafica;"figli";$Figli)
  $Uno:=JSON Stringify($Anagrafica)

  {"nome":"Mario","cognome":"Rossi","figli":{"nome":"Giovanni","anni","12"}}

$Due:=JSON Stringify($Anagrafica;*)
  
  {
  "nome":"Mario",
  "cognome":"Rossi",
  "figli":{
    "nome":"Giovanni",
    "anni","12"
  }
}
Comandi Comandi [v14] I comandi nativi JSON: JSON Parse
Dalla versione v14 sono disponibili i comandi JSON, che permettono di passare da stringhe a oggetti 4d.

Gli oggetti JSON sono circondati da graffe {}
Gli array sono circondati da quadre []
I dati sono in formato "chiave":"valore"
La sequenza di dati è separata dalle virgole

Il valore può essere:

  • testo (fra virgolette)

  • numero (col punto decimale)

  • data ( "YYYY-MM-DDTHH:mm:ssZ" )

  • true/false

  • null



JSON Parse ( jsonString {; type} ) -> Function result

Se viene passato il parametro il comando lo converte nel formato richiesto, altrimentii farà la conversione al meglio possibile. Due esempi:

JSON Parse("{\"nome\":124}") -> ritorna un C_OBJECT con {nome = 124}
JSON Parse("{\"nome\":124}", is longint) -> ritorna 124
Plugin Plugin Creare un documento Excel da 4D
Ecco un esempio di codice per la creazione di un foglio Excel usando il plugin free e opensource scritto da Miyako:

https://github.com/miyako/4d-plugin-xls

$text:=Unicode_sample

    //create a workbook
$book:=XLS WORKBOOK Create

$sheetName:=$text
$sheet:=XLS WORKBOOK Create sheet ($book;$sheetName)

    //problem with Mac version of Excel 2010; OK on Windows
XLS WORKSHEET SET COL WIDTH ($sheet;0;20*256)

$row:=0 //zero-based
$col:=0 //zero-based
$format:=0 //NULL=default format (0x0F)
$cell:=XLS WORKSHEET Set cell text ($sheet;$row;$col;$format;$text)
XLS CELL RELEASE ($cell) //we don't need this reference any more, so release it.

    //create a range reference node
$cell1:=XLS WORKSHEET Set cell real ($sheet;0;1;$format;1)
$cell2:=XLS WORKSHEET Set cell real ($sheet;1;1;$format;2)
$area:=XLS WORKBOOK Create area node ($book;$cell1;$cell2;XLS_CELL_ABSOLUTE_As1;XLS_CELLOP_AS_REFERENCE)
XLS CELL RELEASE ($cell1)
XLS CELL RELEASE ($cell2)

    //create a function node
$fn:=XLS WORKBOOK Create fn1 node ($book;XLS_FUNC_SUM;$area)
$cell:=XLS WORKSHEET Set cell fn ($sheet;2;1;$format;$fn)
XLS NODE RELEASE ($fn)
XLS NODE RELEASE ($area)
XLS CELL RELEASE ($cell)

XLS WORKSHEET RELEASE ($sheet)

$success:=XLS WORKBOOK Save document ($book;System folder(Desktop)+$text+".xls")

XLS WORKBOOK CLEAR ($book)
Info Info Privacy e cookies
COOKIE LAW
      1) Che cosa sono i cookie?
I cookie sono dei file di testo che i siti visitati inviano al browser dell'utente e che vengono memorizzati per poi essere ritrasmessi al sito alla visita successiva.

      2) A cosa servono i cookie?
I cookie possono essere usati per monitorare le sessioni, per autenticare un utente in modo che possa accedere a un sito senza digitare ogni volta nome e password e per memorizzare le sue preferenze.

      3) Cosa sono i cookie tecnici?
I cookie cosiddetti tecnici servono per la navigazione e per facilitare l'accesso e la fruizione del sito da parte dell'utente. I cookie tecnici sono essenziali per esempio per accedere a Google o a Facebook senza doversi loggare a tutte le sessioni. Lo sono anche in operazioni molto delicate quali quelle della home banking o del pagamento tramite carta di credito o per mezzo di altri sistemi.

      4) I cookie Analytics sono cookie tecnici?
In altri termini i cookie che vengono inseriti nel browser e ritrasmessi mediante Google Analytics o tramite il servizio Statistiche di Blogger o similari sono cookie tecnici?. Il Garante ha affermato che questi cookie possono essere ritenuti tecnici solo se "utilizzati a fini di ottimizzazione del sito direttamente dal titolare del sito stesso, che potrà raccogliere informazioni in forma aggregata sul numero degli utenti e su come questi visitano il sito. A queste condizioni, per i cookie analytics valgono le stesse regole, in tema di informativa e consenso, previste per i cookie tecnici."

      5) Che cosa sono i cookie di profilazione?
Sono cookie utilizzati per tracciare la navigazione dell'utente per creare profili sui suoi gusti, sulle sue preferenze, sui suoi interessi e anche sulle sue ricerche. Vi sarà certamente capitato di vedere dei banner pubblicitari relativi a un prodotto che poco prima avete cercato su internet. La ragione sta proprio nella profilazione dei vostri interessi e i server indirizzati opportunamente dai cookie vi hanno mostrato gli annunci ritenuti più pertinenti.

      6) È necessario il consenso dell'utente per l'installazione dei cookie sul suo terminale?
Per l'installazione dei cookie tecnici non è richiesto alcun consenso mentre i cookie di profilazione possono essere installati nel terminale dell'utente solo dopo che quest'ultimo abbia dato il consenso e dopo essere stato informato in modo semplificato.

      7) In che modo gli webmaster possono richiedere il consenso?
Il Garante per la Privacy ha stabilito che nel momento in cui l'utente accede a un sito web deve comparire un banner contenente una informativa breve, la richiesta del consenso e un link per l'informativa più estesa come quella visibile in questa pagina su che cosa siano i cookie di profilazione e sull'uso che ne viene fatto nel sito in oggetto.

      8) In che modo deve essere realizzato il banner?
Il banner deve essere concepito da nascondere una parte del contenuto della pagina e specificare che il sito utilizza cookie di profilazione anche di terze parti. Il banner deve poter essere eliminato solo con una azione attiva da parte dell'utente come potrebbe essere un click.

      9) Che indicazioni deve contenere il banner?
Il banner deve contenere l'informativa breve, il link alla informativa estesa e il bottone per dare il consenso all'utilizzo dei cookie di profilazione.

      10) Come tenere documentazione del consenso all'uso dei cookie?
È consentito che venga usato un cookie tecnico che tenga conto del consenso dell'utente in modo che questi non abbia a dover nuovamente esprimere il consenso in una visita successiva al sito.

      11) Il consenso all'uso dei cookie si può avere solo con il banner?
No. Si possono usare altri sistemi purché il sistema individuato abbia gli stessi requisiti. L'uso del banner non è necessario per i siti che utilizzano solo cookie tecnici.

      12) Che cosa si deve inserire nella pagina informativa più estesa?
Si devono illustrare le caratteristiche dei cookie installati anche da terze parti. Si devono altresì indicare all'utente le modalità con cui navigare nel sito senza che vengano tracciate le sue preferenze con la possibilità di navigazione in incognito e con la cancellazione di singoli cookie.

      13) Chi è tenuto a informare il Garante che usa cookie di profilazione?
Il titolare del sito ha tale onere. Se nel suo sito utilizza solo cokie di profilazione di terze parti non occorre che informi il Garante ma è tenuto a indicare quali siano questi cookie di terze parti e a indicare i link alle informative in merito.

      14) Quando entrerà in vigore questa normativa?
Il Garante ha dato un anno di tempo per mettersi in regola e la scadenza è il 2 Giugno 2015.

COOKIE UTILIZZATI IN QUESTO SITO
File di log: Come molti altri siti web anche questo fa uso di file di log registra cioè la cronologia delle operazioni man mano che vengono eseguite. Le informazioni contenute all'interno dei file di registro includono indirizzi IP, tipo di browser, Internet Service Provider (ISP), data, ora, pagina di ingresso e uscita e il numero di clic. Tutto questo per analizzare le tendenze, amministrare il sito, monitorare il movimento dell'utente dentro il sito e raccogliere dati demografici, indirizzi IP e altre informazioni. Tale dati non sono riconducibili in alcun modo all'identità dell'utente e sono cookie tecnici.

COOKIE DI TERZE PARTI PRESENTI NEL SITO

Cookie: www.sviluppo4d.it usa i cookie per memorizzare le informazioni sulle preferenze dei visitatori e sulle pagine visitate dall'utente e per personalizzare il contenuto della pagina web in basa al tipo di browser utilizzato e in funzione delle altre informazioni che appunto tale browser invia.

1) Cookie Google: tale cookie è stato introdotto da Google cinque anni fa e serve per pubblicare annunci Adsense su www.sviluppo4d.it in modo da mostrare all'utente della pubblicità più mirata in base alla loro cronologia. Gli utenti possono accettare tale cookie oppure disattivarlo nel loro browser visitando la pagina Privacy e Termini. Ulteriori informazioni sulle tipologie e sull'uso che fa Google dei cookie con finalità pubblicitarie possono essere trovate nella pagina web dei Tipi di cookie utilizzati. Anche alcuni partner pubblicitari di Google Adsense possono usare cookie e Web Beacons per tracciare gli utenti.

2) Cookie di Facebook: Questo sito ha alcuni plugin di Facebook che possono tracciare il comportamento dei lettori. Per avere maggiori informazioni si può consultare la pagina per la
Politica della Privacy di Facebook.

3) Google+ attraverso lo script plusone.js potrà trattare i dati personali secondo queste line guida
Google Cookie Policy Privacy

In questo blog hanno accesso server di terze parti o di rete pubblicitarie che usano tale tecnologia per inviare gli annunci nel tuo browser attraverso www.sviluppo4d.it. Tramite il rilevamento del tuo indirizzo IP mostrano la pubblicità più pertinente con i tuoi interessi. Tecnologie simili (quali cookie, web bacons e javascript) possono essere utilizzati dalle reti pubblicitarie di terze parti per misurare l'efficacia dei loro messaggi pubblicitari e per personalizzare il contenuto di tali messaggi.

Il sito www.sviluppo4d.it e il suo amministratore non hanno alcun controllo sui cookie che vengono utilizzati da terze parti quindi per approfondire il tema si consiglia di consultare le politiche della privacy di queste terze parti così come le opzioni per disattivare la raccolta di queste informazioni. L'amministratore di questo sito non può quindi controllare le attività degli inserzionisti di questo blog. È comunque possibile disabilitare i cookie direttamente dal proprio browser.

Queste disposizioni del Garante della Privacy non sono solo italiane ma sono comuni a gran parte del paesi europei. Si possono conoscere le regole di ciascuna giurisdizione consultando il sito Your Online Choices nella propria lingua.

UTILIZZO DI GOOGLE ANALYTICS IN QUESTO SITO
Come detto i cookie analytics sono considerati tecnici se utilizzati solo a fini di ottimizzazione e se gli IP degli utenti sono mantenuti anonimi. Informiamo l'utente che questo sito utilizza il servizio gratuito di Google Analytics. Ricordiamo che i dati vengono usati solo per avere i dati delle pagine più visitate, del numero di visitatori, i dati aggregati delle visite per sistema operativo, per browser, ecc. Gli IP di Google Analytics sono stati anonimizzati. Questi parametri vengono archiviati nei server di Google che ne disciplina la Privacy secondo queste linee guida.

Un utente può disattivare Google Analytics durante la navigazione utilizzando il componenete aggiuntivo disponibile per Chrome, Firefox, Internet Explorer, Opera e Safari.

Si possono inoltre
cancellare i cookie singolarmente per ciascun dominio,
nascondere le ricerche.
settare le impostazioni degli annunci di Google.

NAVIGAZIONE ANONIMA

Una navigazione senza l'utilizzo di cookie tecnici e di profilazione è possibile mediante quella che viene definita navigazione in incognito e che è fattibile con tutti i principali browser.
Ulteriori informazioni sulla disabilitazione dei cookie su Firefox, in inglese.
Ulteriori informazioni sulla disabilitazione dei cookie su Chrome, in inglese
Ulteriori informazioni sulla disabilitazione dei cookie su Internet Explorer, in inglese
Ulteriori informazioni sulla disabilitazione dei cookie su Safari , in inglese
Ulteriori informazioni sulla disabilitazione dei cookie su Opera, in inglese.
Info Info Versioni di OpenSSL usate da 4D
Le librerie OpenSSL usate da 4d e 4d server e il corrispondente supporto a TLS sono i seguenti:

4D openSSL TLS supportato
12.x : 0.9.8j : TLS v1.0
13.x : 1.0.0.d : TLS v1.0
14.x : 1.0.0.d : TLS v1.0
Info Info Posizione della cartella Preferenze di 4d
Dalla versione v12 queste sono le posizioni dei file di preferenza:

WINDOWS
C:\Users\\AppData\Roaming\4D\
C:\Users\\AppData\Roaming\4D Server\

MAC
erano per la v12:
/Users//Library/Preferences/4D/
adesso sono:
/Users//Library/Application Support/4D/ /Users//Library/Application Support/4D Server/


Le posizioni delle finestre e altre dimensioni si trovano:

WINDOWS
C:\Users\Administrator\AppData\Roaming\4D\4D Window Bounds v14\

MAC
/Users//Library/Application Support/4D/4D Window Bounds v14/
Info Info 4D v13.5 : non sono permessi campi unici senza indice
Dalla versione 13.5 4d non salverà più i record che hanno il flag di unico, ma che non hanno un indice assegnato.

Questa procedura permette l'estrazione dalla struttura dei campi unici non indicizzati
cfr http://kb.4d.com/assetid=77024


C_LONGINT($maxTableNumber_l;$currentTable_l)
C_LONGINT($maxFieldCount_l;$currentField_l)
C_LONGINT($dontCare_l) // Per valori GET FIELD PROPERTIES non usati.
C_BOOLEAN($dontCare_f;$isIndexed_f;$isUnique_f)
C_TEXT($logHeader_t;$logRecord_t;$logfile_t)
C_TEXT($delim_t;$lf_t)
C_TIME($logfile_h)
C_TEXT($tableName_t;$fieldName_t;$note_t)

$delim_t:=Char(Tab)
$lf_t:=Char(Carriage return)+Char(Line feed)

$logHeader_t:="Campi unici senza indice:"+$lf_t

$logfile_t:=Get 4D folder(Logs Folder)+"UniciNonIndicizzati"

$logfile_h:=Create document($logfile_t)

If (OK=1)

  SEND PACKET($logfile_h;$logHeader_t)

  $maxTableNumber_l:=Get last table number

  For ($currentTable_l;1;$maxTableNumber_l)
    If (Is table number valid($currentTable_l))
      $maxFieldCount_l:=Get last field number(Table($currentTable_l))
      For ($currentField_l;1;$maxFieldCount_l)
        If (Is field number valid($currentTable_l;$currentField_l))

           // Nota che la seguente riga è spezzata in due:
          GET FIELD PROPERTIES($currentTable_l;$currentField_l;$dontCare_l;\
           $dontCare_l;$isIndexed_f;$isUnique_f;$dontCare_f)

          If (($isUnique_f) & (Not($isIndexed_f)))

           $tableName_t:=Table name(Table($currentTable_l))
           $fieldName_t:=Field name(Field($currentTable_l;$currentField_l))

           $logRecord_t:="["+$tableName_t+"]"+$fieldName_t+$lf_t

           SEND PACKET($logfile_h;$logRecord_t)

          End if
        End if
      End for
    End if
  End for

  CLOSE DOCUMENT($logfile_h)
  SHOW ON DISK($logfile_t)
End if
Web Web Header di risposta da Web
Quando si usa 4D come Web server, normalmente si risponde con le normali pagine come richieste dall'utente con un leader di default (= 200)

Quando invece bisogna rispondere con una pagina che non è quella richiesta, la cosa migliore per rispondere ai casi eccezionali sarebbe quella di mettere il corretto header.

Ad esempio, se l'url non è riconosciuto puoi mandare l'utente alla pagina che vuoi, dovresti impostare questo header

ARRAY TEXT($atHeaders;2)
ARRAY TEXT($atValues;2)
$atHeaders{1}:="X-VERSION"
$atValues{1}:="HTTP/1.1"
$atHeaders{2}:="X-STATUS"
$atValues{2}:="404 Not Found"
WEB SET HTTP HEADER($atHeaders;$atValues)
WEB SEND FILE("pagina_errore.html")

Un altro header utile:
"302 Moved Temporarily" (equivalente al redirect) cfr http://www.sviluppo4d.it/Detail_Faq_Display?ID=632&title=Alternativa+al+comando+SEND+HTTP+REDIRECT

Per una funzione non disponibile si dovrebbe usare:
"501 Not implemented"

Da notare che usualmente si vedono due tipi di errore gestiti automaticamente da 4D Web Server:
"500 Internal server error" : il codice ha incontrato una condizione non prevista, di solito errore puro
"503 Service Unavailable" : hai superato il numero di richieste contemporanee accettate ( di solito legato ad un parametro di configurazione )

Qui l'elenco ufficiale:
http://www.w3.org/Protocols/rfc2616/rfc2616-sec10.html
Novita' Novita' 4d su Mac OS X 10.9
Su Mac OS X 10.9 Maverick le applicazioni che non sono davanti (in 'foreground') vengono automaticamente abbassate di priorità con la nuova funzione "App Nap".

Questo è ovviamente un problema se il 4d Server viene usato su un 10.9: in questo caso basta andare nella finestra Info del Finder per l'app Server e impostare il flag "Impedisci App Nap"
Tecniche Tecniche Testo di un pulsante su più righe
E' possibile scrivere il testo di un pulsante in modo che venga visualizzato su due righe. Non bisogna usare il classico Carriage Return, bensì il carattere "\" ("\\" nel code editor).

Questa caratteristica è utilizzabile solo su 3D button, 3D radio button e 3D check box.
Info Info Ripristino della configurazione di backup
Se si sceglie di effettuare manualmente il backup con il metodo:

CONFIRM("Eseguo il salvataggio automatico?";"Sì";"No")
If (OK=1)
  BACKUP
End if

Se la destinazione non è disponibile (ad esempio una pen drive) 4D modifica il percorso di backup con la cartella della struttura.
Se si salva in un file BackupSave.xml la copia della configurazione corretta, si può modificare il metodo precedente così:

CONFIRM("Eseguo il salvataggio automatico?";"Sì";"No")
If (OK=1)
  DELETE DOCUMENT(getPathname (Structure file)+"Preferences\\Backup\\Backup.xml")
  COPY DOCUMENT(getPathname (Structure file)+"Preferences\\Backup\\BackupSave.xml";u_PathnameOnly (Structure file)+"Preferences\\Backup\\Backup.xml")
  BACKUP
End if
Comandi Comandi Impostare il livello di cifratura SSL
Il comando SET DATABASE PARAMETER con l'opzione 64 (SSL Cipher List) permette di impostare il livello di cifratura SSL.

Ad esempio è possibile eseguire questo codice per disabilitare l'SSL2 e attivare solo l'SSL3 per la versione di 4d v11:

STOP WEB SERVER
SET DATABASE PARAMETER (SSL Cipher List;"HIGH:!SSLv2:!EXP:!ADH:!aNULL:!eNULL:!NULL")
START WEB SERVER

Alcune informazioni su come scrivere questo parametro si può leggere la documentazione del Comando Ciphers di OpenSSL.

Nella v13.3 i valori di default sono:
TLSv1/SSLv3, Cipher : AES256-SHA AES(256)

Le altre cifrature accettate sono
SSL2 ciphers: nessuno

SSL3 ciphers:
CAMELLIA256-SHA 256 bit
AES256-SHA 256 bit
DES-CBC3-SHA 168 bit
SEED-SHA 128 bit
CAMELLIA128-SHA 128 bit
AES128-SHA 128 bit
IDEA-CBC-SHA 128 bit
RC4-SHA 128 bit
RC4-MD5 128 bit
DES-CBC-SHA 56 bit

TLS1 ciphers:
CAMELLIA256-SHA 256 bit
AES256-SHA 256 bit
DES-CBC3-SHA 168 bit
SEED-SHA 128 bit
CAMELLIA128-SHA 128 bit
AES128-SHA 128 bit
IDEA-CBC-SHA 128 bit
RC4-SHA 128 bit
RC4-MD5 128 bit
DES-CBC-SHA 56 bit
Info Info Posizione delle risorse locali *
Nelle installazioni Client/Server 4D registra in locale delle risorse che velocizzano i collegamenti successivi e che sono automaticamente aggiornati senza che l'utente debba intervenire.

Dalla v11 la posizione di queste risorse è in cartelle registrate in queste posizioni:

Windows XP:
{Disco:}\Documents and Settings\{nome_utente}\Local Settings\Application Data\Nomeprogramma_indirizzo

Windows Vista/7
{Disco:}\Users\{nome_utente}\AppData\Local\4D\Nomeprogramma_indirizzo

Mac
{Disco}Users\{nome_utente}\Libreria\Cache\4D\Nomeprogramma_indirizzo


Nel caso in cui il client è incorporato nell'applicazione, le cartelle locali sono:

Windows Vista/7
{Disk}:\Users\{nome_utente}\AppData\Local\Nomeprogramma

Mac OS X
{Disk}:Users:{nome_utente}:Library:Caches:Nomeprogramma Client


Nel caso ci fossero problemi di accesso a queste cartelle è sempre possibile creare una cartella "ClientLocal" vicino al 4d usato come client

Su installazioni Terminal Server/Citrix purtroppo le cartelle Local e Local Settings non possono essere inserite nel profilo, quindi ogni connessione richiederebbe di scaricare sempre le informazioni dal server. Se si vuole evitare questo è possibile integrare direttamente la cartella dell'applicazione (contenente una sottocartella ClientLocal) nel profilo di roaming. Ovviamente in questo caso deve esserci un 4d usato come client per ogni utente collegato.
Tecniche Tecniche Duplicazione RECORD con SUBRECORD
Le nuove versioni di 4D non duplicano piu' i subrecord all'interno del record ed il campo id_added_by_converter non e' editabile, ne avevo necessita' per cui ho fatto questa routine (spero esista un metodo piu' semplice)

Occorre preventivamente creare un set del record da duplicare ed un set del record duplicato (ricordarsi di cancellarli al termine) poi :

DuplicaSubrecords (->[TABLE];->[TABLE]Subrecord;->[TABLE_Subrecord];->[TABLE_Subrecord]id_added_by_converter)


    // DUPLICAZIONE SUBRECORDS

$p_TABLErecord:=$1 // Puntatore alla Tabella RECORD
$p_TABLEsubrecord:=$2 // Puntatore al campo SUBRECORD DEL RECORD
$p_TABLENewsubrecord:=$3 // Puntatore alla Tabella NEW SUBRECORD
$p_Id:=$4 // Puntatore al campo id_added_by_converter

$NumeroTABLE:=Table($p_TABLENewsubrecord)
USE SET("OLD") // Richiamo il record originale
QUERY($p_TABLENewsubrecord->;$p_Id->=Get subrecord key($p_TABLEsubrecord->))
$NumeroSubrecords:=Records in selection($p_TABLENewsubrecord->)
If ($NumeroSubrecords>0)
  USE SET("NEW") // Richiamo il record copia
  For ($i;1;$NumeroSubrecords)
    CREATE SUBRECORD($p_TABLEsubrecord->) // Creo l'aggancio al record
  End for
  SAVE RECORD($p_TABLErecord->)
  USE SET("OLD")
  $NumeroCampi:=Get last field number($p_TABLENewsubrecord)
  ARRAY POINTER($p_ElementoCampo;$NumeroSubrecords*$NumeroCampi)
  For ($i;1;$NumeroSubrecords)
    $P_RECORD:=($i-1)*$NumeroCampi
    For ($k;1;$NumeroCampi)
      $P_Vettore:=$P_RECORD+$k // Calcolo l'elemento del vettore
      $p_ElementoCampo{$P_Vettore}:=Get pointer("$var_"+String($k)) // Associo il puntatore
      $p_Campo:=Field($NumeroTABLE;$k) // Puntatore al campo da prelevare
      $p_ElementoCampo{$P_Vettore}->:=$p_Campo-> // Momorizzo il valore del campo sulla variabile
    End for
    NEXT RECORD($p_TABLENewsubrecord->)
  End for
  USE SET("NEW")
  QUERY($p_TABLENewsubrecord->;$p_Id->=Get subrecord key($p_TABLEsubrecord->))
  $NumeroSubrecords:=Records in selection($p_TABLENewsubrecord->)
  ON ERR CALL("ErrorIdSubrecord") // Filtra Errore su assegnazione campo id_added_by_converter (Metodo ErrorIdSubrecord VUOTO)
  For ($i;1;$NumeroSubrecords)
    $P_RECORD:=($i-1)*$NumeroCampi
    For ($k;1;$NumeroCampi)
      $P_Vettore:=$P_RECORD+$k // Calcolo l'elemento del vettore
      $p_Campo:=Field($NumeroTABLE;$k) // Puntatore al campo da assegnare
      $p_Campo->:=$p_ElementoCampo{$P_Vettore}-> // Assegno il valore dalla variabile al campo
    End for
    SAVE RECORD($p_TABLENewsubrecord->)
    NEXT RECORD($p_TABLENewsubrecord->)
  End for
  UNLOAD RECORD($p_TABLENewsubrecord->)
  ON ERR CALL("")
  SAVE RECORD($p_TABLErecord->) // Forse non serve
End if
2
Info Info Dove si trovano i file 4DLINK
Il file .4DLINK indica il percorso e le opzioni per aprire una struttura, sia per 4d Mono che Server.

Vengono creati automaticamente da 4D per mostrarli nei recenti, ma si possono recuperare, riutilizzare direttamente ed eventualmente modificare.

Per le versioni v11 e v12 il file si trovano nella cartella:

• Windows Vista: C:\Users\UserName\AppData\Roaming\4D\Favorites v11\
• Windows XP: C:\Documents and Settings\UserName\Application
Data\4D\Favorites v11\
• Mac OS: Users/UserName/Library/Preferences/4D/Favorites v11/


Per la versione v13:

• Windows: C:\Users\UserName\AppData\Roaming\4D\Favorites v13\

• Mac OS: /Users/UserName/Library/Application Support/4D/Favorites v13/

dove UserName è il nome dell'utente del sistema.
Info Info Adattare pagine web su Safari IOS
Quando predisponete una pagina web che deve essere visualizzata su device mobili con browser HTML5, tipo Safari Mobile su iPhone e iPad, è possibile utilizzare un metatag specifico che adatta il contenuto allo schermo.

La riga va inserita sotto il tag <head> :

<meta name="viewport" content="width=device-width, initial-scale=1, user-scalable=no">

Per ulteriori informazioni guarda la Safari Web Content Guide
Codice Codice Percorso della cartella Documenti
Nella v13:

$0:=System Folder(Documents folder)


Nella v12:

// Codice di Maurizio Zanni

C_TEXT($_vt_Path)

C_LONGINT($_vl_Type)
$_vl_Type:=Desktop

//Ritorna il Path del Desktop
$_vt_Path:=System Folder($_vl_Type)

//Elimina il Separatore alla fine del Path
$i:=Length($_vt_Path)
$_vt_Path:=Substring($_vt_Path;1;$i-1)

//Ritorna il Path alla cartella Padre del Desktop
$i:=Length($_vt_Path)
While ($_vt_Path≤$i≥#Folder Separator) & ($i>0)
        $i:=$i-1
End while
$_vt_Path:=Substring($_vt_Path;1;$i-0)

//Aggiunge la Cartella Documents con il Separatore finale (Mac o Win)
$_vt_Path:=$_vt_Path+"Documents"+Folder Separator

$0:=$_vt_Path
Info Info Caratteri non utilizzabili in un testo in modalità Unicode
A partire dalla versione 11 è possibile utilizzare il database in modalità Unicode.

In questa modalità, tre caratteri non possono essere memorizzati all'interno di un testo della base dati, e sono:
0
65534 (FFFE)
65535 (FFFF)
Tecniche Tecniche Limite fisico degli indici B-Tree per i campi Text
È importante sapere che, quando si indicizza un campo di tipo testo, il limite fisico di un indice B-Tree è 1024 caratteri, e dunque se si indicizzano contenuti di lunghezza maggiore la ricerca può fallire.
Nessun limite invece per quel che riguarda gli indici a keyword.
Info Info La linea laterale del Method Editor
Sulla parte sinistra del nuovo Method Editor di 4D è possibile vedere, accanto al numero di linea, una piccola linea verticale colorata.

Il colore è verde per indicare che la riga non è stata modificata dall'ultimo salvataggio, giallo se la riga è stata modificata.
Info Info Come utilizzare con 4d i lettori barcode per iPhone, iPod, iPad
I lettori di barcode della Infinite Peripherals, Linea Pro per iPhone e iPod e Infinea Tab per iPad, sono quelli usati da Apple nei propri AppleStore.

Per utilizzarli con un nostro gestionale in 4d (a parte scrivere un'app nativa in IOS) potete usare una soluzione abbastanza semplice:
- da 4d pubblicate via web le pagine con le funzioni che vi servono
- scaricare l'app "Nexus Barcode" che è gia pubblicata su AppStore
- nelle impostazioni dell'app mettete l'indirizzo della vostra pagina di 4d

A questo punto quando aprite l'app si carica la pagina web del vostro gestionale:
- fate click (ops tap) sul campo da inserire
- premete il tasto del lettore di barcode che va a scrivere nel campo

Oppure:
- nella preparazione della pagina predisponete un javascript che si aspetta un solo parametro, tipo: function loadBarcode(codice) {}
- nelle impostazioni dell'app scrivete il nome della funzione: loadBarcode

In questa modalità non c'è bisogno di cliccare (tappare) il campo dove inserire il codice, basta usare il lettore del barcode; a parte la comodità di un passaggio in meno, l'altro vantaggio è che non serve far apparire la tastiera che sullo schermo dell'iPod significa guadagnare un bel po' di spazio

Per l'hardware dei lettori ci si può rivolgere al distributore italiano dei lettori barcode per iPhone, iPod, iPad Cidroid, divisione della Nexus.

Cidroid LineaPro

Cidroid InfineaTab
Info Info Limitare il numero di caratteri in una variabile alfanumerica
Una delle prime FAQ pubblicate su Sviluppo4D riguardava la limitazione del numero di caratteri inseribili in un campo Alpha.

Eccone una versione aggiornata:
Case of
      : (Form event=On After Edit)
      If (Length(Get edited text)>=vLength)
           $textvariable:=Substring(Get edited text;1;vLength)
      End if
End case

A vLength può essere sostituita la lunghezza del campo, nei casi in cui è possibile.

A $textvariable potrebbe essere sostituito Self->, ma in questo caso il metodo non funziona ad esempio sugli elementi degli array di una listbox.
1
Info Info Eseguire VB Script in 4D
Utilizzando il metodo di Keisuke Miyako per l'esecuzione di un VB script, vediamo un esempio di conversione di un file utilizzando MS Word.

Ecco il testo di uno script che apre un file e lo salva in formato solo testo:


Set objWord = CreateObject("Word.Application")
Set objDoc = objWord.Documents.Open("C:\codici.doc")
Const wdFormatText = 2
objDoc.SaveAs "C:\codici.txt", wdFormatText
objWord.Quit




Ecco il metodo (lievemente modificato) di Keisuke Miyako:

C_TEXT($1;$0)
C_BLOB($2)

C_LONGINT($platform_l)
PLATFORM PROPERTIES($platform_l)

If ($platform_l=Windows)

$script_folder_path_t:=Get 4D folder(Current Resources folder)+"vbs\\"
$script_file_path_t:=$script_folder_path_t+Replace string($1;"/";"\\";*)

      If (Test path name($script_file_path_t)=Is a document)

           SET ENVIRONMENT VARIABLE("_4D_OPTION_HIDE_CONSOLE";"true")
           C_BLOB($standard_input_x;$standard_output_x;$standard_error_x)

           If (Count parameters=2)
           $standard_input_x:=$2
           //patch to counter data loss caused by CRLF's in input stream
           SET BLOB SIZE($standard_input_x;BLOB size($standard_input_x)+8166;0)
           End if

           LAUNCH EXTERNAL PROCESS("cscript //Nologo //U \""+$script_file_path_t+"\"";$standard_input_x;$standard_output_x;$standard_error_x)
           $standard_output_t:=Convert to text($standard_output_x;"UTF-16LE")

           If (BLOB size($standard_error_x)#0) & (BLOB size($standard_output_x)=0)
           $0:=Convert to text($standard_error_x;"UTF-16LE")
           Else
           $0:=$standard_output_t
           End if

      End if

End if
Tecniche Tecniche Cosa fare se 4D si chiude all'avvio
Alcune istruzioni veloci su come intervenire nel caso in cui 4d si chiude all'avvio.

Se si riesce ad entrare come Designer basterà verificare la procedura di startuo legata allo specifico user.

Se non si riesce proprio ad entrare, provare una delle seguenti opzioni (una alla volta!)

1. Sei sicuro di essere entrato come Designer?
Se per caso hai impostato un utente di default non fa andare in ambiente Designer e quindi potrebbe non intercettare eventuali errori.
Per verificare tieni premuto il maiuscolo al lancio del programma, così ti appare la lista degli utenti.

2. provare a togliere temporaneamente i plugin

3. cancellare le preferenze di 4d (attento a tenere il file delle licenze)

4. fare una verifica della struttura con 4d tools

5. recuperare l'ultima copia della struttura funzionante

5. provare a verificare la struttura con un'utilitty tipo SanityCheck della Committedsoftware, che funziona fino alla versione 2004.5
http://www.committedsoftware.com/sanitycheck.html
Info Info 4d e Mountain Lion
Ecco i risultati dei primi test con le versioni di 4d v12.4 e v13.1 su Mountain Lion.

Restano aperti solo quattro problemi, che saranno sicuramente fissati nelle prossime versioni (subito nelle prossime versioni Hotfix per i Partner):

2 problemi importanti riguardano un crash dopo:
- il cambio di stampante da codice
- la stampa dagli editor di Etcihette o dal Quick Report

2 solo cosmetici riguardano:
- il comando OBJECT SET ENABLE non mette in grigio il testo disabilitato
- il colore della selezione di record nelle maschere di tipo Lista

Inoltre 4d sas dovrà gestire in qualche modo la nuova gestione di Gatekeeper, per certificare le prossime versioni di 4d con Apple.

Da segnalare un problema con il web server Apache integrato in OS X: se la condivisione web era attiva prima di aggiornare a Mountain Lion, 4d non pèotrà accedere alla porta 80 con il suo Web server.
Purtroppo in Mountain Lion non c'è più la preferenza che stoppa Apache, per cui bisognerà interromperlo con il comando da terminale:
sudo apachectl stop

cfr http://www.4d.com/blog/information-4d-mountain-lion.html
Codice Codice Come creare una plist per Mac OS X o IOS
Il formato XML plist è lo standard con cui sono scritte ad esempio le preferenze dei vari programmi su Mac OS X.

Il formato è letto nativamente dalle applicazioni scritte in XCode, come ad esempio programmi per iPhone e iPad.

L'esempio successivo mostra come creare una plist per ad esempio inviare dei dati strutturati ad un applicativo su iOS in modo semplice e veloce: infatti un xml generico andrebbe ogni volta interpretato, mentre la plist può essere letta direttamente come un NSDictionary.

    ` ----------------------------------------------------
    ` User name (OS): Umberto Migliore
    ` Date and time: 08-02-11, 12:22:51
    ` ----------------------------------------------------
    ` Method: nx_crea_plist
    ` Description:
    `mostra come creare un documento plist:
    `
    `<?xml version="1.0" encoding="UTF-8" standalone="no" ?>
    `<!DOCTYPE plist PUBLIC "-//Apple//DTD PLIST 1.0//EN" "http://www.apple.com/DTDs/PropertyList-1.0.dtd">
    `<plist version="1.0">
    ` <dict>
    ` <key>Author</key>
    ` <string>William Shakespeare</string>
    ` <key>Title</key>
    ` <string>Macbeth</string>
    ` <key>Lines</key>
    ` <array>
    ` <string>It is a tale told by an idiot,</string>
    ` <string>Full of sound and fury, signifying nothing.</string>
    ` </array>
    ` <key>Birthdate</key>
    ` <integer>1564</integer>
    ` </dict>
    `</plist>
    `
    ` Parameters
    ` ----------------------------------------------------
C_TEXT($autore;$titolo)
C_LONGINT($annonascita)
ARRAY TEXT($citazione;0)

    `=== preparo i dati
$autore:="William Shakespeare"
$titolo:="Macbeth"
$annonascita:=1564
APPEND TO ARRAY($citazione;"It is a tale told by an idiot,")
APPEND TO ARRAY($citazione;"Full of sound and fury, signifying nothing.")

    `=== creo la plist
$xml_ref:=DOM Create XML Ref("plist";"";"version";"1.0")

    `=== dict principale che contiene tutto
$dict:=DOM Create XML element($xml_ref;"dict")

$key:=DOM Create XML element($dict;"key")
DOM SET XML ELEMENT VALUE($key;"Author")
$value:=DOM Create XML element($dict;"string")
DOM SET XML ELEMENT VALUE($value;$autore)

$key:=DOM Create XML element($dict;"key")
DOM SET XML ELEMENT VALUE($key;"Title")
$value:=DOM Create XML element($dict;"string")
DOM SET XML ELEMENT VALUE($value;$titolo)

$key:=DOM Create XML element($dict;"key")
DOM SET XML ELEMENT VALUE($key;"Lines")
$array:=DOM Create XML element($dict;"array")
For ($riga;1;Size of array($citazione))
  $value:=DOM Create XML element($array;"string")
  DOM SET XML ELEMENT VALUE($value;$citazione{$riga})
End for

$key:=DOM Create XML element($dict;"key")
DOM SET XML ELEMENT VALUE($key;"Birthdate")
$value:=DOM Create XML element($dict;"integer")
DOM SET XML ELEMENT VALUE($value;$annonascita)

    `=== chiude l'xml, aggiunge lo header e ritorna un testo
DOM EXPORT TO VAR($xml_ref;$testo)
DOM CLOSE XML($xml_ref)
$header:="<!DOCTYPE plist PUBLIC \"-//Apple//DTD PLIST 1.0//EN\" \"http://www.apple.com/DTDs/PropertyList-1.0.dtd\">\n"
$testo:=Insert string($testo;$header;Position("<plist";$testo))

ALERT($testo)  ` $0:=$testo
1
Comandi Comandi Indentazione XML
Quando si crea un XML, i comandi di 4d lo creano con tutta l'indentazione necessaria ad una più facile lettura.

Però se l'xml è utilizzato per comunicazioni via wan (server to server, mobile to server, etc) in produzione è meglio risparmiare in dimensioni dei messaggi, ad esempio togliendo l'indentazione: su file complessi con molti livelli di indentazione il risparmio può essere notevole.

Fino alla v11 si usava il secondo parametro del comando
DOM SET XML OPTION

Dalla v12 il comando è stato rinominato
DOM SET XML OPTION -> DOM SET XML DECLARATION

e invece per le opzioni più generiche si usa il comando più specifico
XML SET OPTION

Questo comando ha diverse opzioni, nel caso specifico la sintassi è:

DOM SET XML OPTION($idRef; XML Indentation; XML No indentation)
Codice Codice Ruotare un'immagine usando SVG
Questo codice è un esempio di utilizzo del componente incluso in 4d per gestire i comandi SVG; lo scopo del metodo è di ruotare di 90 gradi un'immagine, ad esempio per raddrizzare una foto.

    // ----------------------------------------------------
    // User name (OS): Umberto Migliore
    // Date and time: 14-02-11, 11:41:14
    // ----------------------------------------------------
    // Method: nx_svgRuotaQuartoDestra
    // Description
    // Ruota un immagine di un quarto di giro a destra
    //
    // Parameters
    // ----------------------------------------------------

C_PICTURE($immagine;$1;$0)
$immagine:=$1
PICTURE PROPERTIES($immagine;$larga;$alta)
$maggiore:=Choose($larga>$alta;$larga;$alta)
$svg:=SVG_New ($alta;$larga)
$img:=SVG_New_embedded_image ($svg;$immagine;0;0)
SVG_SET_TRANSFORM_ROTATE ($svg;90;$maggiore/2;$maggiore/2)
If ($larga>$alta)
  SVG_SET_TRANSFORM_TRANSLATE ($svg;0;$larga-$alta)
End if
$immagine:=SVG_Export_to_picture ($svg;0)
SVG_CLEAR ($svg)
$0:=$immagine
1
Info Info Autenticazione SOAP
I servizi webservice possono richiedere l'autenticazione in diversi modi:

1. lo standard http BASIC
-> si può usare il comando WEB SERVICE AUTHENTICATE ("nome", "password"; 1)

2. lo standard http DIGEST
-> si può usare il comando WEB SERVICE AUTHENTICATE ("nome", "password"; 2)

3. uno dei due standard http, ma con un proxy http intermedio:

-> si usa il doppio comando
WEB SERVICE AUTHENTICATE ("nome", "password", 0) `può essere BASIC o DIGEST
WEB SERVICE AUTHENTICATE ("nome2", "password2", *) `dati di accesso al proxy

4. può essere del tipo SOAP HEADER, cioè inclusa nel corpo della SOAP

$xml:=DOM Create XML Ref("auth")
$utente:=DOM Create XML element($xml;"username")
DOM SET XML ELEMENT VALUE($utente;"nome")
$password:=DOM Create XML element($xml;"password")
DOM SET XML ELEMENT VALUE($password;"password")
SET WEB SERVICE OPTION(Web Service SOAP Header,$xml)
DOM CLOSE XML($xml)

Sono eventualmente da verificare che i nodi siano "username" e "password"
Codice Codice InStrRev trova la prima posizione di una stringa partendo dalla fine
Come tutti sappiamo, la funzione Position() di 4D restituisce la posizione del primo carattere trovato, ma partendo da sinistra.

In giro ho trovato questo ciclo per trovare la posizione della prima occorrenza di una stringa inclusa in un'altra a partire dalla destra della stringa.
Un po come InStrRev di VB.

C_INTEGER($Pos;$start;$lengthfound;$Result)
C_TEXT($Temp)
  // Ricava la path della cartella preferenze
// Replace string(... Aggiunge un Folder separator alla stringa ricavata:
// Se Folder separator esiste, sostituisce i due Folder separator risultanti con una stringa vuota
// Se Folder separator esiste, non fa' nulla
// In ogni caso la stringa ricavata non terminerà con Folder separator !

$Temp:=Replace string(Get 4D folder(Active 4D Folder)+Folder separator;Folder separator+Folder separator;"")
$Pos:=0
$start:=1
Repeat
  $Result:=Position(Folder separator;$Temp;$start;$lengthfound)
  If ($Result#0)
    $Pos:=$Result
  End if
  $start:=$start+$lengthfound
Until ($Result=0)
<>g_PathPreferences:=Substring($Temp;1;Length(Substring($Temp;1;$Pos)))+Folder separator

Sicuramente funzionante ma, a mio avviso, un po' troppo 'contorta'.
Si puo' semplificare cosi:
    
C_TEXT($Temp)
C_INTEGER($Conta)
    // Ricava la path della cartella preferenze
$Temp:=Replace string(Get 4D folder(Active 4D Folder)+Folder separator;Folder separator+Folder separator;"")
For ($Conta;Length($Temp);1;-1)
  If (Substring($Temp;$Conta;1)=Folder separator)
    $Temp:=Substring($Temp;1;$Conta)
    $Conta:=1
  End if
End for
<>g_PathPreferences:=$Temp+Folder separator

Oltre ad avere un codice piu' leggibile, abbiamo risparmiato tre righe di procedura (a parte le note) e dichiarato
due variabili contro cinque della procedura precedente.

Non è il massimo ...ma con la crisi che incombe, bisogna centellinare tutto.
Codice Codice Creare un file di testi UTF-8 con BOM
Il testo in 4d era in Mac ASCII fino alla versione 2004, dalla v11 è in UTF-16 (a meno che non abbiate lasciato la compatibilità Ascii del db)

Quando si esporta un file in Unicode è necessario normalmente indicare in che formato sono i caratteri che occupano 2 byte (big-endian o little-endian) con un prefisso chiamato un BOM (Byte Order Mark). In realtà in UTF-8 si tende ad esportare i caratteri in byte singoli quindi in linea di massima non ci sono problemi di ordinamento dei byte, e non servirebbe.

Però alcune applicazioni se lo aspettano comunque, quindi a volte è necessario aggiungerlo, come ad esempio con questa procedura:

C_TEXT($1;$testo_t)
C_BLOB($blob_b;$bom_b)

$testo_t:=$1

SET BLOB SIZE($bom_b;3)
$bom_b{0}:=239 ` EF
$bom_b{1}:=187 ` BB
$bom_b{2}:=191 ` BF

CONVERT FROM TEXT($testo_t;"UTF-8";$blob_b)

$doc_h:=Create document("")
If (OK=1)
  SEND PACKET($doc_h;$bom_b)
  SEND PACKET($doc_h;$blob_b)
  CLOSE DOCUMENT($doc_h)
End if
Info Info [v13] Nuova funzionalità dei comandi statistici
I comandi Average, Max, Min, Std deviation, Sum, Sum squares e Variance che storicamente si usavano solo nella stampa dei rapporti (dopo un comando Accumulate) adesso funzionano anche sulla selezione corrente o su un array.

L'effetto sulla ottimizzazione del codice è immediato:
dove prima ad esempio per avere la somma di un array occorreva ciclare sui suoi elementi adesso basta scrivere $totale:=Sum($array).
Codice Codice Metodo alternativo per riempire una Popup Drop Down List
Uso questo metodo nell'oggetto Popup cmb_Cliente che risolve velocemente il problema del valore di default quando un database è completamente vuoto.

Case of
  : (Form event=On Load)
  ARRAY LONGINT(cmb_CodCliente;0)
  ARRAY TEXT(cmb_Cliente;0)

  ALL RECORDS([Clienti])
  SELECTION TO ARRAY([Clienti]Progressivo;cmb_CodCliente; [Clienti]Rag_Sociale;cmb_Cliente)
  If (Size of array(cmb_CodCliente)>0)
    If (Is new record([Stabilimenti])) //nuova scheda
      cmb_Cliente:=1 //mostra un valore di default
      cmb_CodCliente:=cmb_Cliente
    Else //se ci sono record, cerca il codice nel vettore cmb_CodCliente
      // Se Find in array ritorna -1 verrà generato un errore
      // Usando Abs(Find in array..... restituira -1 (quindi 1)
      cmb_CodCliente:=Abs(Find in array(cmb_CodCliente;[Stabilimenti]Cod_Cliente))
      cmb_Cliente:=cmb_CodCliente
    End if
  End if
  : (Form event=On Clicked)
  If (cmb_Cliente#0)
    cmb_CodCliente:=cmb_Cliente
    [Stabilimenti]Cod_Cliente:=cmb_CodCliente{cmb_CodCliente}
  End if
  : (Form event=On Unload)
    CLEAR VARIABLE(cmb_CodCliente)
    CLEAR VARIABLE(cmb_Cliente)
End case

Codice Codice Data e ora in formato XML
Il formato XML di data e ora è il seguente AAAA-MM-GGTHH:MM:SS.

Il vantaggio di questa stringa è che è ordinabile, contiene la coppia dei dati e quindi è più leggibile del timestamp (che rimane però più efficiente e consuma meno spazio su disco e in memoria come indice).

Ecco alcuni trucchi per convertire una data in questo formato e viceversa (non documentati, mi sembra) in 4d 2004.3:

Date("2006-01-24T00:00:00") -> ritorna effettivamente la data 24 gen 2006: Importante funziona solo se la stringa è lunga 19 caratteri e la data è seguita dal separatore "T".

String(current date;8) -> ritorna la data nel formato XML completa di separatore = "2006-01-24T00:00:00"

Per avere data e ora si può usare questa semplice riga:

Replace string(String(Current date;8);"00:00:00";String(Current time;1))

1
Codice Codice Copiare negli appunti il contenuto di una listbox
Il seguente metodo prende come parametri il puntatore ad una listbox, il separatore fra le colonne e quello fra le righe per inserire negli appunti il contenuto della listbox stessa.


C_POINTER($1;$lbPointer_p)
C_TEXT($2;$columnSeparator_t)
C_TEXT($3;$rowSeparator_t)

C_LONGINT($i;$numRows_l)
C_LONGINT($j;$numCols_l)
C_TEXT($theData_t)

ARRAY TEXT($colNames_at;0)
ARRAY TEXT($headerNames_at;0)
ARRAY POINTER($colVars_ap;0)
ARRAY POINTER($headerVars_ap;0)
ARRAY BOOLEAN($colsVisible_ab;0)
ARRAY POINTER($styles_ap;0)

$lbPointer_p:=$1

If (Count parameters>1)
      $columnSeparator_t:=$2
        $rowSeparator_t:=$3
Else
        $columnSeparator_t:=Char(Tab)
        $rowSeparator_t:=Char(Carriage return)
End if

LISTBOX GET ARRAYS($lbPointer_p->;$colNames_at;$headerNames_at;$colVars_ap;$headerVars_ap;$colsVisible_ab;$styles_ap)

$numRows_l:=Size of array($colVars_ap{1}->)
$numCols_l:=Size of array($colNames_at)

For ($i;1;$numRows_l)

      For ($j;1;$numCols_l)
           If ($colsVisible_ab{$j}=True)
           If ($j#1)
           $theData_t:=$theData_t+$columnSeparator_t
           End if
           $theData_t:=$theData_t+String($colVars_ap{$j}->{$i})
           End if
      End for

      If ($i<$numRows_l)
           $theData_t:=$theData_t+$rowSeparator_t
      End if

End for

SET TEXT TO PASTEBOARD($theData_t)


-----------

Da notare come:
- il comando String può ricevere come parametro anche una stringa;
- per testi grandi il testo potrebbe essere prima inserito il un BLOB.
Codice Codice Estrarre tutti gli URL da un testo
Col seguente metodo l'array URL_a viene popolato con gli URL che contiene myString:

ARRAY LONGINT(posFound_a;0)
ARRAY LONGINT(lengthFound_a;0)
ARRAY TEXT(URL_a;0)
C_LONGINT($start)
C_TEXT($mySubstring;myString;$1)
C_TEXT(stringNew; pattern)
C_BOOLEAN($found)

myString:=$1
$start:=1
$found:=False
pattern:="(http|https|ftp)" ` http o https, o ftp.
pattern:=pattern + "\\://" ` ://
pattern:=pattern + "[a-zA-Z0-9\\-\\.]+" `la prima parte del dominio
pattern:=pattern + "\\.[a-zA-Z]{2,4}" `la seconda parte
pattern:=pattern + "(:[a-zA-Z0-9]*)?/?" `la porta e lo slash
pattern:=pattern + "([a-zA-Z0-9\\-\\._?\\,'/\\+%\\$#\\=~\\:\\&])*" `Caratteri riservati
pattern:=pattern + "[^\\.\\,\\)\\(\\s\\']" `Caratteri da escludere
Repeat
  $found:=Match regex(pattern;myString;$start;posFound_a;lengthFound_a)
  stringNew:=Substring(myString;posFound_a{0};lengthFound_a{0})
  If ($found)
    APPEND TO ARRAY(URL_a;stringNew)
  End if
  $start:=posFound_a{0}+lengthFound_a{0}
Until (Not($found))
Codice Codice Ottenere la lista di tutte le relazioni di un database
Utilizzando i comandi SQL è possibile interrogare la tabella di sistema _USER_CONSTRAINTS per ottenere in una listbox chiamata ListBox_Relations l'elenco di tutte le relazioni presenti in un database o loro proprietà.

-----
Un primo esempio

Begin SQL
  SELECT * FROM _USER_CONSTRAINTS
  WHERE CONSTRAINT_TYPE = 'P'
  INTO LISTBOX :miaListBox;
End SQL

per ottenere l'elenco di tutte le chiavi primarie utilizzate.
-----
Un secondo esempio

Begin SQL
  SELECT * FROM _USER_CONSTRAINTS
  WHERE CONSTRAINT_TYPE = 'R'
  INTO LISTBOX :miaListBox;
End SQL

per l'elenco delle chiavi esterne.

-----
Per l'elenco completo


Begin SQL
  SELECT * FROM _USER_CONSTRAINTS
  INTO LISTBOX :miaListBox;
End SQL
Comandi Comandi Esempi di Replicate in Campi e in Array
Il comando Replicate eseguito su un 4d (Locale) permette di leggere da un 4d (Remoto) gli ultimi record modificati/cancellati dopo l'ultima lettura, per avere la "replica" aggiornata di alcune tabelle.

La sintassi minima è la seguente:

REPLICATE campoid, campo1, campo2, campo3
FROM tabellaRemota
FOR REMOTE STAMP :vRemote_Stamp
REMOTE OVER LOCAL
LATEST REMOTE STAMP :vRiceviCorrenteUltimoStam
INTO localTable(campoid, campo1, campo2, campo3);

Da notare che in vRiceviCorrenteUltimoStam arriva l'ultimo Stamp corrente da conservare per usarlo nella prossima volta che si fa la stessa chiamata.

Questa di seguito è la versione completa dei parametri opzionali, con una clausola WHERE per selezionare i record da tagliare, con la LIMIT e la conseguente OFFSET per tagliare l'esportazione in più parti

REPLICATE campoid, campo1, campo2, campo3
FROM tabellaRemota
WHERE tabellaRemota.campox=:vCondizione
LIMIT :vLimit_Value
OFFSET :vOffset_Value
FOR REMOTE STAMP :vRemote_Stamp,
LOCAL STAMP :vLocal_Stamp
REMOTE OVER LOCAL /*oppure LOCAL OVER REMOTE*/
LATEST REMOTE STAMP :vRiceviCorrenteUltimoStam,
LATEST LOCAL STAMP :vLatest_Local_Stamp
INTO localTable(campoid, campo1, campo2, campo3);


Da notare che posso leggere i dati senza doverli appoggiare su una tabella, ma solo in array.

REPLICATE campoid, field1, field2, field3
FROM remoteTable
FOR REMOTE STAMP :vRemote_Stamp
REMOTE OVER LOCAL
LATEST REMOTE STAMP :vRiceviCorrenteUltimoStam
INTO :aID, :aCampo1, :aCampo2, :aCampo3;


REPLICATE campoid, campo1, campo2, campo3
FROM tabellaRemota
WHERE tabellaRemota.campox=:vCondizione
LIMIT :vLimit_Value
OFFSET :vOffset_Value
FOR REMOTE STAMP :vRemote_Stamp,
LOCAL STAMP :vLocal_Stamp
REMOTE OVER LOCAL /*oppure LOCAL OVER REMOTE*/
LATEST REMOTE STAMP :vRiceviCorrenteUltimoStam,
LATEST LOCAL STAMP :vLatest_Local_Stamp
INTO :aID, :aCampo1, :aCampo2, :aCampo3;


Comandi Comandi [v13] Confronta due Immagini con Equal Pictures
Il comando Equal pictures confronta precisamente due immagini per dimensione e contenuto.

Il comando accetta 3 parametri: i primi 2 sono le immagini da confrontare, nel terzo si troverà un'immagine maschera con a nero i pixel identici.

Ritorna False se le dimensioni non sono uguali.

Se le dimensioni sono uguali il confronto è a livello di pixel, e nella maschera viene 'acceso' in bianco il pixel che segna la differenza.
        
Se non ci sono differenze neanche a livello di pixel il comando ritorna True.
Info Info Sintassi 4D SQL : INNER JOIN
Una inner join crea una griglia di dati combinando i valori delle due tabelle di partenza (A and B) basandosi su una certa regola di confronto: la query compara ogni riga della tabella A con ciascuna riga della tabella B cercando di soddisfare la regola di confronto definita.

Esempio, date queste due tabelle di dati:

Tabella Aziende
ID_AziendaRagione_Sociale
az1Nexus
az2Acme
az3NewCo


Tabella Contatti
CognomeID_Azienda
Rossiaz1
Verdiaz2
Bianchi-


Begin SQL
    SELECT Cognome,Aziende.Ragione_Sociale
    FROM Contatti
    INNER JOIN Aziende
    ON Contatti.ID_Azienda=Aziende.ID_Azienda
    INTO :ArrCognome,:ArrRagSociale;
End SQL

Risultato:
CognomeRagione_Sociale
RossiNexus
VerdiAcme
1
Info Info Sintassi 4D SQL : RIGHT OUTER JOIN
Una right outer join (o right join) semplicemente ricalca il funzionamento della left outer join, ma invertendo l'ordine delle tabelle interessate.
Il risultato di una query right outer join per le tabelle A e B contiene sempre tutti i record della tabella di destra ("right") B, mentre vengono estratti dalla tabella di sinistra ("left") A solamente le righe che trovano corrispondenza nella regola di confronto della join. Questo significa che se la clausola ON trova 0 (zero) righe in A, la join mostrerà una riga risultante con valore NULL in tutte le colonne corrispondenti al risultato per le colonne di A.

Esempio, date queste due tabelle di dati:

Tabella Aziende
ID_AziendaRagione_Sociale
az1Nexus
az2Acme
az3NewCo


Tabella Contatti
CognomeID_Azienda
Rossiaz1
Verdiaz2
Bianchi-


  Begin SQL
    SELECT Cognome,Aziende.Ragione_Sociale
    FROM Contatti
    RIGHT OUTER JOIN Aziende
    ON Contatti.ID_Azienda=Aziende.ID_Azienda
    INTO :ArrCognome,:ArrRagSociale;
End SQL

Risultato:
CognomeRagione_Sociale
NULL NewCo
RossiNexus
VerdiAcme
Info Info Sintassi 4D SQL : LEFT OUTER JOIN
Il risultato di una query left outer join (o semplicemente left join) per le tabelle A e B contiene sempre tutti i record della tabella di sinistra ("left") A, mentre vengono estratti dalla tabella di destra ("right") B solamente le righe che trovano corrispondenza nella regola di confronto della join. Questo significa che se la clausola ON trova 0 (zero) righe in B, la join mostrerà una riga risultante con valore NULL in tutte le colonne corrispondenti al risultato per le colonne di B.

Esempio, date queste due tabelle di dati:

Tabella Aziende
ID_AziendaRagione_Sociale
az1Nexus
az2Acme
az3NewCo


Tabella Contatti
CognomeID_Azienda
Rossiaz1
Verdiaz2
Bianchi-


  Begin SQL
    SELECT Cognome,Aziende.Ragione_Sociale
    FROM Contatti
    LEFT OUTER JOIN Aziende
    ON Contatti.ID_Azienda=Aziende.ID_Azienda
    INTO :ArrCognome,:ArrRagSociale;
End SQL

Risultato:
CognomeRagione_Sociale
BianchiNULL
RossiNexus
VerdiAcme
Info Info SIntassi 4D SQL : FULL OUTER JOIN
Una full outer join combina i risultati delle due tabelle A e B tenendo conto di tutte le righe delle tabelle, anche di quelle che non hanno corrispondenza tra di loro.
Il risultato di una query full outer join per le tabelle A e B contiene sempre tutti i record della tabella di sinistra ("left") A, estraendo dalla tabella di destra ("right") B solamente le righe che trovano corrispondenza nella regola di confronto della join; inoltre verranno estratti tutti i record della tabella di sinistra ("left") A che non trovano corrispondenza nella tabella di destra ("right") B impostando a NULL i valori di tutte le colonne della tabella B e tutti i record della tabella di destra ("right") B che non trovano corrispondenza nella tabella di sinistra ("left") A impostando a NULL i valori di tutte le colonne della tabella A.

Esempio, date queste due tabelle di dati:

Tabella Aziende
ID_AziendaRagione_Sociale
az1Nexus
az2Acme
az3NewCo


Tabella Contatti
CognomeID_Azienda
Rossiaz1
Verdiaz2
Bianchi-

Begin SQL
    SELECT Cognome,Aziende.Ragione_Sociale
    FROM Contatti
    FULL OUTER JOIN Aziende
    ON Contatti.ID_Azienda=Aziende.ID_Azienda
    INTO :ArrCognome,:ArrRagSociale;
End SQL

Risultato:
CognomeRagione_Sociale
NULL NewCo
BianchiNULL
RossiNexus
VerdiAcme
Info Info Evoluzione delle list box
  • 4D 2004: solo array, 19 comandi
  • v11: selezioni, SQL, formule, 22 comandi aggiuntivi
  • v12: multistile
  • v13: footer, blocco colonne, controllo dell'inserimento, allineamento verticale
Info Info Sintassi 4D SQL : CROSS JOIN
Una cross join fornisce le basi attraverso cui tutti i tipi di inner join operano.
Il risultato di una cross join è il prodotto cartesiano di tutte le righe delle tabelle che concorrono alla query di join. E' come dire che stiamo facendo una inner join senza impostare la regola di confronto o in cui la regola di confronto ritorna sempre vero.


Esempio, date queste due tabelle di dati:

Tabella Aziende
ID_AziendaRagione_Sociale
az1Nexus
az2Acme
az3NewCo


Tabella Contatti
CognomeID_Azienda
Rossiaz1
Verdiaz2
Bianchi-


Begin SQL
    SELECT Cognome,Aziende.Ragione_Sociale
    FROM Contatti
    CROSS JOIN Aziende
    ON Contatti.ID_Azienda=Aziende.ID_Azienda
    INTO :ArrCognome,:ArrRagSociale;
End SQL

Risultato:
Cognome Ragione_Sociale
Bianchi Nexus
Bianchi Acme
Bianchi NewCo
Rossi Nexus
Rossi Acme
Rossi NewCo
Verdi Nexus
Verdi Acme
VerdiNewCo
Info Info Verifica da codice se Quicktime è installato
Su Windows le librerie di Quicktime potrebbero non essere installate e alcune immagini registrate con un 4d precedente alla v11 potrebbero non vedersi:
  appare in questo caso un riquadro bianco con la scritta del tipo "Quicktime is required"

ARRAY TEXT($identificativi_at;0)
ARRAY TEXT($nomi_at;0)
PICTURE CODEC LIST($identificativi_at;$nomi_at)
If (Find in array($identificativi_at;".qtif")<0) // Tipo immagine QuickTime
  ALERT("QuickTime non è installato.")
End if
Info Info Convertire le immagini per non dipendere da Quicktime
A partire dalla v11 le immagini in 4D non sono più salvate come PICT, ma nel loro formato originale.
Però se abbiamo convertito un db ci potrebbero essere delle immagini che richiedono Quicktime per essere viste su Windows.

Per eliminare la necessità di questa installazione è possibile usare il comando CONVERT PICTURE, come ad esempio:

ALL RECORDS([MieImmagini])
While (Not(End selection([MieImmagini])))
  CONVERT PICTURE([MieImmagini]Immagine;".jpg")
  SAVE RECORD([MieImmagini])
  NEXT RECORD([MieImmagini])
End while

Dalla v12 questo comando permette anche la compressione del JPEG con questa sintassi:
CONVERT PICTURE([MieImmagini]Immagine;".jpg"; 0.6)

NOTA: Ovviamente per essere eseguita questa procedura richiede Quicktime, la volta in cui viene eseguita.

cfr Verifica da codice se Quicktime è installato
Info Info iPad e 4D
Le alternative per utilizzare l'ipad o l'iphone collegandolo a 4d sono, partendo dalle più facili:

1. utilizzare un app tipo iSort
pro: l'app costa veramente poco ed è subito disponibile, su 4d serve la licenza web
contro: le possibilità di personalizzazione sono poche, e non si può programmare lato ipad (Apple vieta esecuzione di codice nelle app)

2. preparare delle pagine web servite da 4d, semplici senza usare ajax o framework
pro: sviluppo semplice, il safari di iPad è un browser vero e completo, compatibile con html5, css e javascript
contro: funziona bene se c'è sempre copertura, ad esempio usandolo in area coperta da wifi

3. preparare delle pagine web servite da 4d, utilizzando ajax o framework
pro: puoi usare degli oggetti più completi, come griglie di dati, se perdi il collegamento potresti usare i dati offline
contro: comunque serve la connessione internet attiva, una parte di programmazione si sposta su javascript

4. scrivere un programma in objective c
pro: puoi scriverti un'applicazione completa registrando in locale i dati sui cui lavorare, scaricando e inviando solo ogni tanto gli aggiornamenti
Il collegamento a 4d si può fare :
- via web utilizzando l'url speciale 4DSYNC che sfrutta la funzionalità di sincronizzazione inclusa in v12 (cfr iPhone, iPad, Android e l'url 4DSYNC
- via webservice o semplice chiamata GET (/4daction/metodo/parametro..); 4d può essere sia server che monoutenza con licenza web
- via file: l'app può scaricare un file postato in una directory web con autenticazione, può inviare i dati in formato POST; si perde il realtime, ma non serve licenza web

contro: è più lungo scrivere in obiettive e si devono valutare bene le modalità di distribuzione, che riporto:
- app ad hoc: si installa su un massimo di 100 ipad/iphone per sviluppatore (l'elenco si può resettare solo 1 volta all'anno)
- app enterprise: l'azienda cliente si registra come Enterprise a 299$ anno può installare su quanti ipad vuole
- app su appstore: i limiti sono quelli delle regole di approvazione Apple, i tempi di rilascio sono di 5-10 giorni per ogni aggiornamento
Info Info Cambiare un valore in più record con una solo Update SQL
Questo codice è un esempio di SQL con cui è possibile modificare un valore in diversi record con un unico comando.

Presupponendo che abbiamo una Tabella in cui i record hanno un campo Codice e un campo Nome, il comando nel formato Begin SQL sarà così:

Begin SQL
  update [Tabella]
  set Nome = CASE Codice
  when 1 then 'NomeUno'
  when 2 then 'NomeDue'
  when 3 then 'NomeTre'
  end
  where codice in (1,2,3)
end sql

In pratica:
1) il WHERE campo IN (n,n,..) mi seleziona i record il cui Codice è nella lista
2) il CASE ritorna il valore desiderato al variare del campo Codice
Tecniche Tecniche Creazione manuale di uno "UserSet"
Lo "UserSet" è lo speciale set che contiene i record selezionati dall'utente nel form di output.

Quando serve è comunque possibile popolarlo "manualmente": se abbiamo un array contenente dei record number, è possibile scrivere:

CREATE SET FROM ARRAY([Table];$arrRecNum;"UserSet")

per simulare la selezione dei record "manuale" da parte dell'utente.
Info Info Linee di codice su più righe
Come si può facilmente vedere, dalla 4D v12 è stato radicalmente modificato il method editor. Una delle modifche più utili riguarda la possibilità di poter mandare a capo le righe in modo da avere un'istruzione più facilmente leggibile (si pensi, ad esempio, alle righe infinite di estesi SELECTION TO ARRAY).

Adesso basta far finire una riga col carattere "\" e ill testo della riga successiva verrà considerato come prosecuzione dell'attuale.
Info Info Attributo per Eseguire i metodi sul server
Utilizzando la nuova opzione "Execute in server" per i metodi, bisogna ricordare che:
- il processo sul server non è creato al momento, ma usa il processo "gemello" che gira sul server
- il metodo su server considera i record bloccati e le transazioni, ma non tiene conto dei valori delle variabili sul client e non mantiene la selezione corrente.
- le variabili processo del metodo sul server persistono (con valore) anche dopo la conclusione del processo stesso sul server
- se passi un puntatore ad una variabile o un array il valore puntato è inviato al server e ritornato al client se modificato

Sul server non possono essere eseguiti alcuni comandi: se provi a farlo apparirà una finestra di dialogo. Alcuni comandi non funzionanti nei metodi sono:

Comandi correlati all'interfaccia
ADD RECORD
ADD SUBRECORD
DISPLAY SELECTION
FILTER EVENT
GRAPH TABLE
Level
ON EVENT CALL
MODIFY RECORD
MODIFY SELECTION
MODIFY SUBRECORD
Open external window
QUERY BY EXAMPLE
QR REPORT

Comandi di struttura
CHANGE LICENSES
CREATE DATA FILE
EDIT ACCESS
OPEN DATA FILE
CALL PROCESS

Comandi sulle risorse
SET PICTURE TO LIBRARY
REMOVE PICTURE FROM LIBRARY
SET PICTURE TO LIBRARY

Comandi per la gestione dei menu
APPEND MENU ITEM
Count menu items
Count menus
DELETE MENU ITEM
DISABLE MENU ITEM
ENABLE MENU ITEM
Get menu item
Get menu item key
Get menu item mark
Get menu item style
Get menu title
INSERT MENU ITEM
Menu selected
SET MENU ITEM
SET MENU ITEM SHORTCUT
SET MENU ITEM MARK
SET MENU ITEM STYLE
SHOW MENU BAR

Comandi per la stampa
ACCUMULATE
BREAK LEVEL
PAGE BREAK
PAGE SETUP
PRINT SETTINGS
Printing page
Subtotal
Info Info Scaricare le mail con IMAP
Dovendo scaricare da Gmail gli aggiornamenti del gruppo Sviluppo4D su Facebook inviati da Umberto in occasione del summit di Boston, ecco il codice utilizzato all'uopo.

C_TEXT($msgText)
READ WRITE([Posta])
$error:=0
$sslPOPPort:=995
$Errore_l:=IT_SetPort (13;$sslPOPPort) //12 is for 'SMTP with SSL'
$error:=IMAP_Login ("pop.gmail.com";"indirizzo@gmail.com";"password";$imapID;1)
$error:=IMAP_SetCurrentMB ($imapID; "4D Summit 2011"; $msgNber; $newMsgNber; $customFlags; $permanentFlags; $mbUID)
For ($i;1;$msgNber)
      CREATE RECORD([Posta])
      msgDataItem:="INTERNALDATE"
      $Err:=IMAP_MsgFetch ($imapID;$i;msgDataItem;$text)
      $position:=Position(" ";$text)
      $text:=Delete string($text;1;$position)
      $error:=IMAP_GetMessage ($imapID;$i;0;31000;2;$msgText;1)

      $datrovare:="nel gruppo Sviluppo4D"
      $position:=Position($datrovare;$msgText)
      $msgText:=Delete string($msgText;1;$position+Length($datrovare)+2)

      $datrovare:="Per commentare"
      $position:=Position($datrovare;$msgText)
      $msgText:=Delete string($msgText;$position-2;Length($msgText))
      [Posta]msg_body:=$msgText
      [Posta]msg_ID:=$i
      SAVE RECORD([Posta])
      UNLOAD RECORD([Posta])

End for


$error:=IMAP_Logout($imapID)
Codice Codice Spostare i dati di una tabella in un database esterno
Nella v12 è possibile usare database esterni: ecco un esempio veloce per trasferire i dati da una tabella locale nell'equivalente tabella sul db esterno.


ALL RECORDS([Tabella])
SQL EXPORT SELECTION([Tabella];$percorsoAppoggio)

Begin SQL
      USE DATABASE DATAFILE :$percorsoDB;
End SQL

$importa:=$percorsoAppoggio+Folder separator+
importa:=importa+"SQLExport"+Folder separator
importa:=importa+"Export.sql"

SQL EXECUTE SCRIPT($importa;SQL On error abort)

Begin SQL
      USE DATABASE SQL_INTERNAL;
End SQL
Info Info Salvare le modifiche alla Struttura
Sin dalla versione v11, anche la struttura di 4d è un database e gli aggiornamenti fatti in fase di sviluppo seguono le stesse regole delle modifiche ai dati.

Se 4d si chiude inaspettatamente si potrebbero perdere le ultime modifiche fatti (modifiche al codice o ai form, ad esempio).

Per forzare il salvataggio si può:
- compilare
- passare da Design mode ad Application mode (e viceversa)
- eseguire il comando FLUSH BUFFERS
Codice Codice Stampare una Rich Text Area
Ecco di seguito il codice di un pulsante che permette di stampare il contenuto di una Rich Text Area memorizzato nel campo [Tabella]MioTesto:

Case of
      : (Form event=On Clicked)

      PRINT SETTINGS

      If (ok=1)
           OPEN PRINTING JOB

           If (ok=1)
           OPEN PRINTING FORM("Form1")
           vStampa:=OBJECT Get styled text([Tabella]MioTesto)
           $complete_b:=Print object(*;"vStampa")
           CLOSE PRINTING JOB
           End if
      End if
End case
Comandi Comandi Comandi rinominati dalla v11 alla v12
v11v12
APPLY XSLT TRANSFORMATIONXSLT APPLY TRANSFORMATION
BEST OBJECT SIZEOBJECT GET BEST SIZE
BUTTON TEXTOBJECT SET TITLE
COMPRESS PICTURE FILEQT COMPRESS PICTURE FILE
COMPRESS PICTUREQT COMPRESS PICTURE
Current form pageFORM Get current page
DECODEBASE64 DECODE
DELETE LISTBOX COLUMNLISTBOX DELETE COLUMN
DELETE LISTBOX ROWLISTBOX DELETE ROW
DOM SET XML OPTIONSDOM SET XML DECLARATION
ENCODEBASE64 ENCODE
FIRST PAGEFORM FIRST PAGE
FONT SIZEOBJECT SET FONT SIZE
FONT STYLEOBJECT SET FONT STYLE
FONTOBJECT SET FONT
Get alignmentOBJECT Get alignment
GET FORM OBJECTSFORM GET OBJECTS
GET FORM PARAMETERFORM GET PARAMETER
GET FORM PROPERTIESFORM GET PROPERTIES
Get formatOBJECT Get format
GET LISTBOX ARRAYSLISTBOX GET ARRAYS
GET LISTBOX CELL POSITIONLISTBOX GET CELL POSITION
Get listbox column widthLISTBOX Get column width
Get listbox informationLISTBOX Get information
Get listbox rows heightLISTBOX Get rows height
Get number of listbox columnsLISTBOX Get number of columns
Get number of listbox rowsLISTBOX Get number of rows
GET OBJECT RECTOBJECT GET COORDINATES
GET XML ERRORXML GET ERROR
GET XSLT ERRORXSLT GET ERROR
GOTO AREAGOTO OBJECT
GOTO PAGEFORM GOTO PAGE
INPUT FORMFORM SET INPUT
INSERT LISTBOX COLUMN FORMULALISTBOX INSERT COLUMN FORMULA
INSERT LISTBOX COLUMNLISTBOX INSERT COLUMN
INSERT LISTBOX ROWLISTBOX INSERT ROW
LAST PAGEFORM LAST PAGE
LOAD COMPRESS PICTURE FROM FILEQT LOAD COMPRESS PICTURE FROM FILE
MOVE OBJECTOBJECT MOVE
MOVED LISTBOX COLUMN NUMBERLISTBOX MOVED COLUMN NUMBER
MOVED LISTBOX ROW NUMBERLISTBOX MOVED ROW NUMBER
NEXT PAGEFORM NEXT PAGE
OUTPUT FORMFORM SET OUTPUT
PREVIOUS PAGEFORM PREVIOUS PAGE
SAX SET XML OPTIONSSAX SET XML DECLARATION
SCROLL LINESOBJECT SET SCROLL POSITION
SELECT LISTBOX ROWLISTBOX SELECT ROW
SET ALIGNMENTOBJECT SET ALIGNMENT
SET CHOICE LISTOBJECT SET CHOICE LIST NAME
SET COLOROBJECT SET COLOR
SET ENTERABLEOBJECT SET ENTERABLE
SET FILTEROBJECT SET FILTER
SET FORM HORIZONTAL RESIZINGFORM SET HORIZONTAL RESIZING
SET FORM SIZEFORM SET SIZE
SET FORM VERTICAL RESIZINGFORM SET VERTICAL RESIZING
SET FORMATOBJECT SET FORMAT
SET LISTBOX COLUMN WIDTHLISTBOX SET COLUMN WIDTH
SET LISTBOX GRID COLORLISTBOX SET GRID COLOR
SET LISTBOX ROWS HEIGHTLISTBOX SET ROWS HEIGHT
SET RGB COLORSOBJECT SET RGB COLORS
SET SCROLLBAR VISIBLEOBJECT SET SCROLLBAR
SET VISIBLEOBJECT SET VISIBLE
SET XSLT PARAMETERXSLT SET PARAMETER
SHOW LISTBOX GRIDLISTBOX SHOW GRID
SORT LISTBOX COLUMNSLISTBOX SORT COLUMNS
SQL EXPORTEXPORT ODBC
SQL IMPORTIMPORT ODBC
Comandi Comandi Comandi rinominati dalla 2004 alla v11
v2004.8v11
APPEND TO CLIPBOARDAPPEND DATA TO PASTEBOARD
AsciiCharacter code
CLEAR CLIPBOARDCLEAR PASTEBOARD
Compiled applicationIs compiled mode
Count fieldsGet last field number
Count tablesGet last table number
DELETE ELEMENTDELETE FROM ARRAY
DELETE LIST ITEMDELETE FROM LIST
EXECUTEEXECUTE FORMULA
Find index keyFind in field
GET CLIPBOARDGET PASTEBOARD DATA
GET PICTURE FROM CLIPBOARDGET PICTURE FROM PASTEBOARD
GET PLUGIN LISTPLUGIN LIST
Get text from clipboardGet text from pasteboard
INSERT ELEMENTINSERT IN ARRAY
INSERT LIST ITEMINSERT IN LIST
MENU BARSET MENU BAR
ODBC CANCEL LOADSQL CANCEL LOAD
ODBC End selectionSQL End selection
ODBC EXECUTESQL EXECUTE
ODBC EXPORTSQL EXPORT
ODBC GET LAST ERRORSQL GET LAST ERROR
ODBC GET OPTIONSQL GET OPTION
ODBC IMPORTSQL IMPORT
ODBC LOAD RECORDSQL LOAD RECORD
ODBC LOGINSQL LOGIN
ODBC LOGOUTSQL LOGOUT
ODBC SET OPTIONSQL SET OPTION
ODBC SET PARAMETERSQL SET PARAMETER
SET MENU ITEM KEYSET MENU ITEM SHORTCUT
SET PICTURE TO CLIPBOARDSET PICTURE TO PASTEBOARD
SET TEXT TO CLIPBOARDSET TEXT TO PASTEBOARD
Test clipboardPasteboard data size
USE ASCII MAPUSE CHARACTER SET
Info Info [v12] Cut & Paste tra le strutture delle tabelle
Con la versione 12 è possibile copiare le definizioni dei campi da una tabella ad un'altra.

Nella maschera "Structure" prendi la tabella di partenza, selezioni i campi che ti servono nella nuova tabella.
Effettui il copia (mela+c o ctrl+c).

Selezioni la tabella di destinazione ed effettui incolla (mela+v o ctrl+v).

4D ti genera nella nuova tabella i campo con nomi e proprietà identiche ai campi selezionati nella tabella di partenza.
Info Info Porte TCP per la connessione ad un 4d Server
Per il collegamento da un Client a 4D Server le porte TCP di default sono:

19813 - Application server
19814 - DB4D server
19812 - SQL server

Sulla porta DB4D sono veicolate tutte le chiamate al database, un socket per ogni processo divise poi sul server in thread differenti.
Comandi Comandi [v12.2] Comando FORM SCREENSHOT
Dalla versione 12.2 è disponibile il nuovo comando FORM SCREENSHOT che accetta due sintassi:

FORM SCREENSHOT ( formPict )
Crea una schermata del form corrente al momento

FORM SCREENSHOT ( {aTable ;} formName ; formPict {; pageNum} )
Crea una schermata di un form del database come vista nel form editor
Info Info La posizione attuale in un array di grandi dimensioni
La variabile che indica la posizione attuale in un array è automaticamente dichiarata come Integer, tipo che ha il limite di 32767.
Questo significa che il codice:

ARRAY TEXT($largeArray_at;33000)
For ($i;1;33000)
        $largeArray_at{$i}:=String($i)
End for

$largeArray_at:=32768
$element:=$largeArray_at{$largeArray_at} `errore

produce errore poiché la variabile $largeArray_at non è stato dichiarato.

La soluzione è quindi dichiarare come Longint le variabili da usare come indici di puntatori ad array di grandi dimensioni.
Comandi Comandi Reindirizzare l'output di LAUNCH EXTERNAL PROCESS
I parametri aggiuntivi di LAUNCH EXTERNAL PROCESS permettono di riempire una variabile $output con lo standard output del comando lanciato. Ecco un esempio.

C_TEXT($input;$error;$output)
$input:=Char(34)+$percorsoprogramma+Char(34)+" "+$altriparametri+Char(13)+Char(10)
SET ENVIRONMENT VARIABLE("_4D_OPTION_HIDE_CONSOLE";"true")
LAUNCH EXTERNAL PROCESS("cmd.exe";$input;$output;$error)
Tecniche Tecniche Utilizzare SVG per trovare la dimensione di un testo
E' possibile usare i comandi SVG per conoscere la dimensione di un testo.
Analizziamo il seguente codice:

C_PICTURE(immagine)
C_TEXT(root;ref)
C_LONGINT(larghezza;altezza)
root:=DOM Create XML Ref("svg";"http://www.w3.org/2000/svg")
ref:=DOM Create XML element(root;"text";\ //crea il testo
    "font-family";"Arial";\ //set font
    "font-size";"11";\ //set size
    "font-weight";"bold";\ //set style
    "y";"1em") //set Y coordinate
DOM SET XML ELEMENT VALUE(ref;"TESTO DI ESEMPIO")
SVG EXPORT TO PICTURE(root;immagine;Get XML Data Source)
DOM CLOSE XML(root)
PICTURE PROPERTIES(immagine;larghezza;altezza)

Il codice crea l'XML che corrisponde a un SVG che possiamo poi trasformare in immagine e conoscerne la dimensione.
In larghezza e altezza avremo quindi la dimensione del testo.
Codice Codice Modificare la cella di una listbox con un solo clic
Per modificare l'elemento di una list box è necessario cliccarci due volte: il primo clic seleziona l'elemento, il secondo lo rende modificabile. Per fare in modo che un clic renda direttamente modificabile l'elemento si può uasre il seguente codice:

Case of
: (Form event=On Clicked )
    ` mi asssicuro che non stia facendo una selezione mutipla
      If (Not(Shift down | Windows Ctrl down))
           LISTBOX GET CELL POSITION(*;"ListBox";col;row;colVar_p)
           EDIT ITEM(colVar_p->;row)
      End if
End case


1
Info Info Nomi di variabili riservati
I seguenti nomi di variabili sono riservati a 4D:

C1, C2, C3, C4, C5, ecc. (usate nei report)
OK
Document
Error
KeyCode
Modifiers
MouseDown
MouseProc
MouseX
MouseY
FldDelimit
RecDelimit


Non devono dunque essere usate se non nel contesto cui si riferiscono: ad esempio Document contiene il percorso del documento dopo aver usato Open document, Create document o Append document ed è stato scelto un file mostrando la dialog di sistema per l'apertura o il salvataggio di un file.
1
Info Info 4D v12: help tip automatici per i metodi
Il method editor di 4D nella v12 è stato completamente riscritto.

Tra le novità, una molto utile è la possibilità di visualizzare un help tip per i comandi di 4D, con definizione e una breve descrizione.

Possiamo però fare la stessa cosa anche con i nostri metodi:
  infatti, portando il mouse su un nostro metodo, non abbiamo bisogno di aprirlo per vedere cosa faceva perché come help tip vengono visualizzate tutte le righe iniziali del metodo che siano commento (e che non siano quindi codice).

Prendendo quindi l'abitudine di commentare i metodi nella loro parte iniziale con la loro definizione e descrizione, diventerà agevole conoscere le info sul nostro codice.
Plugin Plugin SVG: conoscere gli attributi di un oggetto
Per conoscere tutte le proprietà legate (e quindi configurabili) di un oggetto in un'area SVG si usa il comando SVG_GET_ATTRIBUTES. Esso popola due array con i nomi e i valori degli attributi. Se $ref è l'ID dell'oggetto, utilizzeremo:

ARRAY TEXT($arrNames;0)
ARRAY TEXT($arrValues;0)
SVG_GET_ATTRIBUTES ($ref;->$arrNames;->$arrValues)
Tecniche Tecniche Visualizzare interamente le variabili testo in fase di debug
Volevo solo segnalarvi un modo per poter vedere il contenuto di variabili testo molto grosse durante il debug.

Io mi sono trovato molto spesso in condizioni simili e devo dire che era molto difficile vedere il contenuto dopo un certo numero di caratteri. Molti usavano (io compreso) la save su disco per poterla aprire poi con un altro programma e controllarne il contenuto. Io mi sono trovato spesso a dover vedere il contenuto di una variabile che era un file xml, ed era molto frustante dover interrompere il debug per mettere codice per salvare il file.

Un metodo semplice ed efficace è quello di usare il comando :

SET TEXT TO PASTEBOARD ( text )

Quindi quando siete fermi sul vostro break nel riquadro delle variabili inserite il comando, sostituite "text" con la vostra variabile e a quel punto avrete il contenuto nel clipboard. Facendo incolla su textedit avrete il contenuto della variabile ben leggibile.
Info Info SVG: inserire un testo con vari formati
Se all'interno della stessa area di testo SVG devo inserire diversi stili (ad esempio una parola in tondo, una seconda in grassetto e una terza in corsivo), il comando da utilizzare è SVG_New_tspan.

A differenza della gran parte dei comandi SVG, SVG_New_tspan prende come parametro l'ID dell'area di testo (definita da un precedente SVG_New_text o SVG_New_tspan) a cui il nuovo testo deve essere collegato.
Quindi:

$textID:=SVG_New_text($SVG;"Scrivere ";10;10;"arial";12)
SVG_SET_FONT_SIZE(SVG_New_tspan($textID;"con ");14)
SVG_SET_FONT_SIZE(SVG_New_tspan($textID;"SVG ");18)
SVG_SET_FONT_SIZE(SVG_New_tspan($textID;"è ");24)
SVG_SET_FONT_SIZE(SVG_New_tspan($textID;"facile");36)

definisce l'area di testo, quindi inserisce il testo aggiuntivo, modificandone al volo la dimensione.
Plugin Plugin SVG: inserire un testo semplice
Il modo più agevole per inserire un testo in un'area SVG è il comando SVG_New_text. Il suo uso più semplice è:

$SVG:=SVG_New
$textID:=SVG_New_text($SVG;"Test SVG")

che crea un'area SVG e scrive il testo desiderato.
I parametri aggiuntivi utilizzabili sono svariati e permettono di impostare posizione del testo, dimensione, allineamento, stile e tipo del carattere, rotazione e spaziatura. La sintassi completa è:

SVG_New_text ( parentSVGObject ; text {; x {; y {; font {; size {; style {; alignment {; color {; rotation {; lineSpacing {; stretching}}}}}}}}}} )

Il comando restituisce l'ID dell'elemento inserito.
Info Info Standard supportati da 4D
Ecco un elenco dei vari standard:
  • esecuzione diretta script PHP 5.3 con interprete compilato FastCGI
  • supporto XML versione 1.0 con la libreria Xerces di Apache Foundation:
    • standard DOM (Document Object Model)
    • il SAX (Simple API XML)
    • la notazione XPath (per il DOM)
    • le trasformazioni tramite XSL style sheets (eXtended Stylesheet Language)

  • gestione di quattro tipi di Metadati per le immagini: EXIF, GPS, IPTC e TIFF
  • gestione del forma SVG ((Scalable Vector Graphics), sia come visualizzazione che creazione e modifica
  • supporto dell'architettura standard XLIFF (XML Localization Interchange File Format) per la localizzazione dell'interfaccia
  • supporto dei WebService, sia come server che come client
    • generazione automatica del WSDL (Web Service Description Language) come server e interpretazione degli stessi per la generazione del codice client
    • protocolli SOAP (versione 1.1 e versione 1.2)
    • RPC (Remote Procedure Call)
    • DOC (Document-oriented)

  • il Web Server è compatibile HTML 1.1, supporta documenti XML, la tecnologia WML (Wireless Markup Language), il protocollo SSL (Secured Socket Layer)
  • supporta le CGI e può essere chiamato tramite CGI da altri web server
  • utilizzando il web server sui client è possibile realizzare facilmente il Load Balancing
  • disponibilità di un 4D AJAX Framework, per creare pagine web collegate e distribuite dal server 4D
  • disponibilità di un 4D for Flex, che permette alle applicazioni Flex di collegarsi direttamente al server 4D
  • supporta le chiamate via ODBC con driver nativo anche per Macintosh
  • supporta le chiamate via PDO_4D da PHP
  • supporta chiamate dirette server to server con altri server 4D
  • può interrogare altri database via protocollo ODBC e OCI (Oracle Call Interface)
  • comandi per interrogazione server posta POP3 e IMAP, Internet Message Access Protocol, Version 4 revision 1 (IMAP4rev1)
  • comandi per l'invio di posta tramite server SMTP, con allegati, destinatari, destinatari in copia CC (Carbon Copy ) e in copia nascosti BCC (Blind Carbon Copy ), autenticazione (CRAM-MD5, LOGIN, PLAIN), commenti
  • comandi per la gestione diretta dei server FTP (File Transfer Protocol)
  • comandi di basso livello TCP/IP, UDP, utility come Ping e Name resolver

Macro Macro [4D Pop] Macro : Incolla Speciale
Fra i vari strumenti della palette 4dPop (inclusa nell'ambiente di sviluppo di 4d) ce n'è uno tanto pieno di funzioni quanto poco notato dai programmatori: 4DPop Macros.

Oggi parliamo della macro Incolla Speciale, che elabora un testo prima di incollarlo nel codice.

Richiamandola, ci sono diverse opzioni da scegliere e un'area di anteprima:

String
Incolla il testo fra apici e sostituendo i caratteri speciali (return, tab, etc) con gli equivalenti per le stringhe (\r, \t, etc)

Comments
Incolla il testo già commentato

HTML
Incolla il testo spezzandolo in diverse righe e aggiungendolo ad una variabile testo (utile per generare via codice una pagina web)



Regex Pattern
Incolla il testo sostituendo i caratteri sinificativi per le regex con l'equivalente con lo slash (ad esempio * diventa \*)

Code from 4D v11 FR
Incolla il testo traducendolo dal codice di 4d in francese, utile ad esempio per utilizzare un metodo inviato sul forum di 4d da un francese.
Macro Macro [4D Pop] Macro : Dichiarazioni
Fra i vari strumenti della palette 4dPop (inclusa nell'ambiente di sviluppo di 4d) ce n'è uno tanto pieno di funzioni quanto poco notato dai programmatori: 4DPop Macros.

Oggi parliamo della sola macro Dichiarazioni, che scrive automaticamente le dichiarazioni per le variabili locali in un metodo.

Richiamandola, appare una maschera dove compare la lista delle variabili:
- In grassetto quelle riconosciute
- In grassetto sottolineato gli array riconosciuti
- In corsivo le variabili da identificare



Alla destra della lista sono elencati i tipi da assegnare alle variabili non riconosciute, con la possibilità di usare la tastiera per velocità.

In realtà, se si usa una nomenclatura delle variabili coerente, esiste la possibilità che questa macro assegni automaticamente i tipi senza il nostro intervento.
Usando il tasto Options si accede ad un pannello dove è possibile indicare lo schema usato nei propri programmi per assegnare i nomi, ad esempio:

Lon_* = indica le variabili che iniziano per "Lon_"
*Lon_* = indica le variabili che contengono "Lon_"
*A(\d\d)_* = indica le variabili che iniziano per A seguita da 2 cifre


Tecniche Tecniche Come aggiungere un campo di ricerca ad un form
Con la v12 sono disponibili degli oggetti (chiamati widget) che sono predisposti per essere riutilizzati nelle maschere di 4d.

Ad esempio si può trovare il campo di ricerca (con stile differente su Mac e su Windows) che si chiama SearchPicker.




Per utilizzarlo si può:
- sceglierlo dalla libreria degli oggetti (nella v12 icona dei libri nella barra superiore del form editor)

oppure

- creare un subform e assegnarli il tipo "Detail" col nome "SearchPicker"

Il subform avrà una variabile che è quella che contiene il valore inserito; il pulsantino di cancellazione è automatico, mentre il testo in grigio (il suggerimento) che sparisce appena si scrive viene definito con il comando SearchPicker SET HELP TEXT.
Info Info Farsi avvisare da 4d se un backup non ha avuto successo
Il backup di solito viene lanciato in ore notturne o quando non ci sono normalmente utenti online; oppure il server non è facilmente accessibile... come fare a controllare che il backup sia andato a posto?

E' possibile utilizzare il metodo Database che si chiama "On Backup Shutdown Database Method"; questo metodo viene chiamato automaticamente alla fine del processo di backup, con parametro $1 = 0 se tutto è andato a posto o # 0 se ci sono stati problemi.

C_LONGINT($1;$backupStatus)
C_DATE($data)
C_TIME($ora)
C_REAL($num)
C_TEXT($status)
$backupStatus:=$1
If ($backupStatus#0)
  GET BACKUP INFORMATION(Last Backup Date ;$data;$ora)
  GET BACKUP INFORMATION(Last Backup Status ;$num;$status)
  $messaggio:="Errore nel backup numero "+string($backupStatus)+"\r\r"
  $messaggio:=$messaggio+"Ultimo Backup: "+String($data)+" alle "+String($ora)+"\r"
  $messaggio:=$messaggio+"Stato: "+$status
  $res:=smtp_quickSend(<>serverSmtp ; <>mailMittente ; <>mailDestinatario ; "Errore durante il backup"; $messaggio)
End if
Info Info [12.2] Nuovi tag HTML
Per maggior chiarezza e miglior controllo, alcuni tag HTML sono stati modificati in 4d v12.2:
  • 4DHTMLVAR è sostituito da 4DHTML
  • 4DVAR -> obsoleto, al suo posto usa:
    -> 4DHTML per inserire una variabile 4d o una espressione come codice Html
    -> 4DTEXT per inserie una variabile 4d o una espressione come testo


Note:
- Adesso 4DTEXT non accetta più il char(1) come primo carattere per interpretarlo come HTML, che era il primo vecchio meccanismo per supportare le due varianti di 4DVAR.
- Le vecchie varianti sono ancora supportate, ma essendo dichiarate obsolete dalla 12.2 sarà meglio cominciare ad usare la nuova nomenclatura
Componenti Componenti [v12] Come utilizzare il componente RichTextArea
Occorre innanzitutto installare il componente "RichTextArea.4dbase" nella cartella Components a livello della struttura .4db o nella cartella Components all'interno dell'applicativo 4d.

Nel form dove si vuole modificare un campo testo va creata una subform, impostando tipo TEXT e una variabile, ad esempio txtMioWord.

Nel metodo legato al form carico la variabile con il contenuto del campo:

Case of
  : (Form event=On Load)
  txtMioWord:=[Tabella]CampoTesto
End case

Mentre nel metodo della subform, aggiorno il campo con la variabile:

Case of
  : (Form event=On Data Change)
  [Tabella]CampoTesto:=txtMioWord
End case
Info Info Errore 10509 alla connessione con un Client
Quando si tenta di connettere un client a un server v11 e successivi e appaiono questi errori:
-10509 Can't open database
1024 Cannot open structure of database
1211 Cannot create connection
4 <<Localizator is not available.>>

Vuol dire che la porta precedente e quella successiva alla porta di comunicazione client/server non sono accessibili, normalmente a causa di un firewall-

La porta di default è la 19813: anche la 19812 e la 19814 dovrebbero essere aperte.
Codice Codice Usare Match Regex per estrarre i numeri da un indirizzo IP
Ecco un esempio di utilizzo di Match regex per verificare un indirizzo tcp/ip ed estrarne le diverse cifre.

Si basa su:
- il simbolo \d = una cifra
- gli operatori {min,max} indicano quante ripetizioni cercare
- gli operatori () isolano i diversi gruppi trovati

    // ----------------------------------------------------
    // User name (OS): Umberto Migliore
    // Date and time: 01-03-11, 23:48:44
    // ----------------------------------------------------
    // Method: regex
    // Description
    //
    //
    // Parameters
    // ----------------------------------------------------

ARRAY LONGINT($arrayPosizioni;0)
ARRAY LONGINT($arrayLunghezze;0)
C_BOOLEAN(isTcpCorretto)
C_TEXT($stringa;$1)

$stringa:=$1` per esempio "192.168.0.1"

isTcpCorretto:=Match regex("(\\d{1,3})\\.(\\d{1,3})\\.(\\d{1,3})\\.(\\d{1,3})";$stringa;1;$arrayPosizioni;$arrayLunghezze)

If (isTcpCorretto)
  $numero1:=Substring($stringa;$arrayPosizioni{1};$arrayLunghezze{1})
  $numero2:=Substring($stringa;$arrayPosizioni{2};$arrayLunghezze{2})
  $numero3:=Substring($stringa;$arrayPosizioni{3};$arrayLunghezze{3})
  $numero4:=Substring($stringa;$arrayPosizioni{4};$arrayLunghezze{4})
End if
Comandi Comandi Un compendio per usare Match regex [2]
Continuiamo ad analizzare i pattern possibili per il comando Match regex.

- {...} definiscono un numero di ripetizioni della espressione regolare precedente. Può essere usato nella forma:
{n} l'espressione regolare deve essere ripetuta n volte
{min,} l'espressione regolare deve essere ripetuta almeno min volte
{min, max} l'espressione regolare deve essere ripetuta almeno min volte e massimo max volte

- | (la pipe) definisce più opzioni (il classico OR). Quindi:

  • "4|d" corrisponde a "d"
  • "4|d" non corrisponde a "b"


- (...) definiscono i gruppi. È possibile infatti ottenere sia delle stringhe di corrispondenza che delle sottostringhe, quelle che corrispondono ai pattern fra parentesi. Quindi:

  • "(4+)d" corrisponde a "4d". Subpattern: {"4"}

Comandi Comandi [v11 SQL] Il comando SET QUERY AND LOCK
Il nuovo comando SET QUERY AND LOCK, che prende come parametro semplicemente True o False, permette di richiedere il blocco dei record risultanti da una query. Ciò garantisce che i risultati di una certa ricerca non possano essere modificato da un altro processo che non sia quello attuale.

Il comando può essere usato solo all'interno di una transazione (altrimenti restituisce errore), e i record restano bloccati finché la transazione viene terminata: a quel punto i record vengono comunque rilasciati. Basta comunque chiamare SET QUERY AND LOCK(False) per rilasciarli.

Ecco un esempio di cancellazione controllata da SET QUERY AND LOCK:

START TRANSACTION
SET QUERY AND LOCK(True)`così mi assicuro che i record trovati siano locked per gli altri
QUERY([Clients];[Clients]Category=“C”)
DELETE SELECTION([Clients])
SET QUERY AND LOCK(False)
VALIDATE TRANSACTION

1
Tecniche Tecniche [v12] Stampare su PDF con SET PRINT OPTION su Windows
Con la v12 è ora possibile usare SET PRINT OPTION per stampare su PDF anche sotto Windows (su Mac il PDF è nativo).

Per farlo è necessario installare il driver gratis (OpenSource) PDFCreator sotto la licenza AFPL (Aladdin Free Public License). Per maggiori informazioni fare riferimento a questo link: Licenza PDFCreator

La versione attualmente certificata è la 0.9.9 che si scarica da qui: PDFCreator 200.9.9.

E' necessario avere i diritti di Amministratore: verrà creata una nuova stampante virtuale chiamata di default "PDFCreator" .
Comandi Comandi Un compendio per usare Match regex [1]
Il comando Match regex permette di fare operazioni particolari sulle stringhe. La forma base del comando è:

Match regex (pattern; aString)

dove aString è la stringa dove effettuiamo la ricerca, pattern è una speciale stringa che contiene "cosa" cercare. Se pattern si trova all'interno di di aString il comando ritorna True. Ecco cosa si può inserire all'interno di pattern.

- il caso più ovvio: se pattern:="mytext" e aString:="mytext", il comando ritorna True

- . (il punto) corrisponde a "qualsiasi carattere" tranne new line (a meno che ciò non venga espressamente specificato nelle opzioni inseribili in pattern). Quindi:

  • "4.b" corrisponde a "4db"
  • "4.r" non corrisponde a "4dfr"


- [ ... ] definisce classi di caratteri. Come:

    "[a-z]" corrisponde a qualsiasi lettera minuscola

Se la classe inizia con "^", la classe viene negata, cioè esiste corrispondenza se i caratteri non sono nella classe.

- * (asterisco) corrisponde a zero o più operatori. Quindi:

  • "z*" corrisponde a ""
  • "z*" corrisponde a "zzzzz"
  • "4d*b" corrisponde a "4dddb"
  • "4d*b" non corrisponde a "4d"


- + (il segno più) corrisponde a uno o più operatori. Quindi:

  • "z+" corrisponde a "z"
  • "z+" non corrisponde a ""


? - corrisponde a zero o un operatore. Quindi:

  • "a?" corrisponde a ""
  • "a?" corrisponde a "a"
  • "a?" non corrisponde a "aa"


Codice Codice [v12] Ricevere mail dal server POP di GMail
Ecco, per finire, un estratto di codice per ricevere mai da GMail dal server POP.


$tempFolder:=Temporary folder
$error:=0
$sslPOPPort:=995
$Errore_l:=IT_SetPort (13;$sslPOPPort) //13 is for 'POP with SSL'
$error:=POP3_SetPrefs (-1;$tempFolder;$tempFolder)
$error:=POP3_BoxInfo ($pop3ID;$msgCount;$msgSize)
ARRAY LONGINT($aMsgSize;0)
ARRAY LONGINT($aMsgNum;0)
ARRAY TEXT($aMsgID;0)
$error:=POP3_MsgLstInfo ($pop3ID;1;$msgCount;$aMsgSize;$aMsgNum;$aMsgID)
For ($msgNumber;1;Size of array($aMsgNum))
`.......
`scarica la mail con POP3_Download
`cancellala con POP3_Delete
`scarica il messaggio con MSG_GetBody
`.......
End for
$error:=POP3_Logout ($pop3ID)

Comandi Comandi Cambiare il set di caratteri usando i comandi SQL
Quando si fanno dei collegamenti a server SQL esterni ci potrebbero essere dei problemi di transcodifica del set di caratteri.

Il comando che permette di definire il charset corretto è
SQL SET OPTION(SQL Charset ;<charset>)

Il valore del parametro charset è di default 106 che sta per UTF-8

Gli altri codici si trovano nella tabella di questa pagina : http://www.iana.org/assignments/character-sets

A volte però non è semplice identificare quale codice usare, per via delle varie combinazioni ad esempio di client usato da mac o da windows: una soluzione è usare il parametro -2 che si adatta alla piattaforma ( e cioè iso-8859-1 su windows e MacRoman su mac).

SQL SET OPTION(SQL Charset ;-2)
Codice Codice [v12] Ricevere mail dal server IMAP di GMail
Ecco di seguito la parte di codice necessaria per autenticarsi al server IMAP di GMail e ricevere l'elenco dei messaggi di posta in entrata.


C_INTEGER($error;$sslIMAPPort)
C_LONGINT($smtp_id)
$tempFolder:=Temporary folder
$sslIMAPPort:=993
$error:=IT_SetPort (14;$sslIMAPPort) //14 is for 'IMAP with SSL'
$error:=IMAP_SetPrefs (-1;$tempFolder) //
$error:=IMAP_Login ("imap.gmail.com";"mymail@gmail.com";"mypassword";$imapID;1)

ARRAY LONGINT($aMsgSize;0)
ARRAY LONGINT($aMsgNum;0)
ARRAY LONGINT($aMsgID;0)
$error:=IMAP_SetCurrentMB ($imapID;"INBOX")
$error:=IMAP_MsgLstInfo ($imapID;1;$msgCount;$aMsgSize;$aMsgNum;$aMsgID)
$error:=IMAP_Logout ($imapID)

Codice Codice [v12] Inviare mail utilizzando il server SMTP di GMail
Grazie alle nuove funzionalità SSL degli Internet Command di 4D v12, è adesso possibile inviare messaggi attraverso il server smtp.gmail.com. Vediamo il metodo:


C_INTEGER($error;$sslSMTPPort)
C_LONGINT($smtp_id)
C_TEXT($smtpHost)
C_TEXT($gmailUser;$gmailPass;$replyTo;$sendEmailTo)
C_TEXT($msg;$subject)

$sslSMTPPort:=465 // port used for SSL SMTP - gmail wants 465
$smtpHost:="smtp.gmail.com" // smtp host for gmail
$gmailUser:="mymail@gmail.com" // gmail user
$gmailPass:="mypass" // gmail password
$replyTo:="mymail@gmail.com" // have replies sent here
$sendEmailTo:="test@sendmail.it" // send email here
$subject:="An example from sviluppo4d" // subject for email
$msg:="It works fine" // email body

$error:=IT_SetPort (12;$sslSMTPPort) //12 is for 'SMTP with SSL'

$error:=SMTP_New ($smtp_id)
$error:=SMTP_Host ($smtp_id;$smtpHost;0)
$error:=SMTP_Auth ($smtp_id;$gmailUser;$gmailPass;0)
$error:=SMTP_AddHeader ($smtp_id;"Importance";"Normal";0)
$error:=SMTP_From ($smtp_id;$gmailUser;1)
$error:=SMTP_ReplyTo ($smtp_id;$replyTo;0)
$error:=SMTP_To ($smtp_id;$sendEmailTo;0)
$error:=SMTP_Subject ($smtp_id;$subject;0)
$error:=SMTP_Body ($smtp_id;$msg;0)
$error:=SMTP_Attachment ($smtp_id;"c:\\aPDFdoc.pdf";7)
$error:=SMTP_Send ($smtp_id;1) //1 to use ssl
$error:=SMTP_Clear ($smtp_id)
Codice Codice Come sapere il giorno in cui cade Pasqua per un certo anno
// ----------------------------------------------------
    // User name (OS): Umberto Migliore
    // Date and time: 09-02-11, 23:55:55
    // ----------------------------------------------------
    // Method: nx_giornoPasquaPerLAnno
    // Description
    // Ritorna il giorno di Pasqua per l'anno
    //
    // Parameters
    // $1 = anno
    // ----------------------------------------------------

c_longint($anno;$1)
C_LONGINT($dopo21marzo)
C_DATE($pasqua;$0)

$anno:=$1
$phpok:=PHP Execute("";"easter_days";$dopo21marzo;$anno)
$pasqua:=Add to date(!00-00-00!;$anno;3;21+$dopo21marzo)
$0:=$pasqua
Info Info [v12] CALL SUBFORM CONTAINER
La v12 integra un comando molto utile per gestire le subform.

CALL SUBFORM CONTAINER(event)

invia un evento event (un longint) all'object method del "contenitore" della subform.
Il comando può trovarsi nel form method della subform o in uno degli oggetti della stessa.
L'evento event può essere o uno dei valori delle costanti 4D per i form events o un qualsiasi valore definito dall'utente (che viene gestito sempre nel metodo del contenitore).

Tale commistione di valori rende consigliabile l'uso di valori negativi per gli eventi "user defined" in modo che non vadano in conflitto con quelli predefiniti 4D.
Info Info [v12] RESTORE
Il comando RESTORE accetta adesso due parametri, entrambi opzionali:
- l'archivio da ripristinare;
- la cartella di destinazione per il ripristino.

Se il ripristino viene eseguito correttamente, la variabile OK viene settata ad uno e la variabile Document conterrà il percorso della cartella di ripristino; in caso contrario OK va a 0 e Document avrà come valore una stringa vuota.
Info Info [v12] I manuali in PDF
4D v12 installa automaticamente i manuali in versione HTLM. Per i fan ("duri a morire" recita il sito di 4D) dei PDF, ecco dove trovare la documentazione nel formato Adobe:

Doc Center homepage

FTP site

In realtà, non si tratta di una reimpaginazione dei manuali: sono esattamente le pagine html in pdf.
Codice Codice Ottenere info sulla memoria
  ` ----------------------------------------------------
    ` User name (OS): Umberto Migliore
    ` Date and time: 02-02-11, 10:51:05
    ` ----------------------------------------------------
    ` Method: nx_GetMemory
    ` Description
    ` se non passi parametri mostra un alert con tutti i valori correnti
    ` se passi una delle seguenti stringhe ritorna il valore specifico in KB
    ` 
    ` es. nx_GetMemory
    ` es. $memoriadisponibile:=nx_GetMemory("Free Memory")
    `
    ` $1 può essere:
    ` "cacheSize"
    ` "usedCacheSize"
    ` "Physical Memory Size"
    ` "Free Memory"
    ` "Used physical memory"
    ` "Used virtual memory"
    ` ----------------------------------------------------


ARRAY TEXT($arrNomi;0)
ARRAY REAL($arrValori;0)
ARRAY REAL($arrOggetti;0)

GET CACHE STATISTICS(1;$arrNomi;$arrValori;$arrOggetti)

If (Count parameters=0)
  
  C_TEXT($testo)
  $testo:=""
  For ($i;1;Size of array($arrValori))
    $testo:=$testo+$arrNomi{$i}+" : "+String($arrValori{$i}/1024;"### ### ### KB")+Char(13)
  End for
  ALERT($testo)

Else  `Count parameters>0

  C_TEXT($1;$parametro)
  $pos:=Find in array($arrNomi;$parametro)
  If ($pos>0)
    $0:=$arrValori{$pos}/1024  `i valori sono in bytes
  Else
    $0:=-1
  End if

End if
Codice Codice Esportazione della struttura usando _USER_COLUMNS
Ecco un esempio di utilizzo della tabella interna _USER_COLUMNS per esportare la struttura del database corrente.

ARRAY TEXT($nomeTabella;0)
ARRAY TEXT($nomeCampo;0)
ARRAY LONGINT($tipoCampo;0)
ARRAY LONGINT($lunghezzaCampo;0)
ARRAY BOOLEAN($accettaNULL;0)
ARRAY LONGINT($idTabella;0)
ARRAY LONGINT($idCampo;0)

Begin SQL
  SELECT * FROM _USER_COLUMNS
  INTO :$nomeTabella, :$nomeCampo, :$tipoCampo, :$lunghezzaCampo, :$accettaNULL, :$idTabella, :$idCampo
End SQL

// ordina per tabella e in secondo livello per idCampo
MULTI SORT ARRAY ($nomeTabella;>;$nomeCampo;$tipoCampo;$lunghezzaCampo;$accettaNULL;$idTabella;$idCampo;>)

$doc:=Create document($System folder(Desktop) +"struttura.text")
$ultimaTabella:=""
For ($i;1;Size of array($nomeTabella))
  If ($ultimaTabella#$nomeTabella{$i})
    SEND PACKET($doc;"\r"+$nomeTabella{$i}+"\t"+String($idTabella{$i})+"\r")
    $ultimaTabella:=$nomeTabella{$i}
  End if
  $tipo:=Choose($tipoCampo{$i};"-";"Boolean";"-";"Integer";"Longint";"Integer 64bits";"Real";"Float";"Date";"Time";"Alpha/Text";"-";"Picture";"-";"-";"-";"-";"-";"BLOB")
  SEND PACKET($doc;$nomeTabella{$i}+"\t"+$nomeCampo{$i}+"\t"+String($tipoCampo{$i})+"\t"+$tipo+"\t"+String($lunghezzaCampo{$i})+"\t"+String($accettaNULL{$i};"True;;False")+"\t"+String($idCampo{$i})+"\r")
End for
CLOSE DOCUMENT($doc)
Codice Codice Esempio di uso di SVG
In 4d è presente l'engine di visualizzazione delle immagini SVG, creabili con i comandi XML e alla fine trasformati in picture con il comando
SVG EXPORT TO PICTURE.

Ecco un esempio:

  C_PICTURE(miaPicture)
  $svg:=DOM Create XML Ref("svg";"http://www.w3.org/2000/svg")
ref:=DOM Create XML element(root;"text";\ //oggetto di tipo testo
    "font-family";"Arial";\ //con questo font
    "font-size";"26";\ //questa dimensione
    "font-weight";"bold") //questo stile
  DOM SET XML ATTRIBUTE($ref;"fill";"red") // posso aggiungere un attributo anche dopo
  DOM SET XML ATTRIBUTE($ref;"y";"1em") // imposto la posizione y
  DOM SET XML ELEMENT VALUE($ref;"Ecco un esempio")
  SVG EXPORT TO PICTURE($svg;miaPicture;Copy XML Data Source)
  DOM CLOSE XML($svg)
Info Info Invio e ricezione mail con protocollo SSL
Dalla versione 12.1 è possibile inviare e ricevere mail utilizzando l'SSL come protocollo di scambio dati.
L'uso del protocollo non richiede nessuna configurazione particolare: basta passare il nuovo parametro a 1 nei seguenti comandi:


  • IMAP_Login
  • SMTP_Send
  • SMTP_QuickSen
  • POP3_Login



Invece il comando IT_SetPort ora accetta 3 nuovi valori per l'impostazione delle porte:
12 = SMTP SSL
13 = POP3 SSL
14 = IMAP SSL
Comandi Comandi Le sintassi del parametro sorgente dati di SQL LOGIN
Il comando SQL LOGIN accetta 3 parametri: la fonte dati, user e password.

La fonte dati può essere espressa in diversi modi:

- IP address
Sintassi: IP:<Indirizzo>{:<PortaTCP>}{:ssl}
In questo caso il comando apre una connessione diretta al 4D Server eseguito sulla macchina indicata dall'IP. Se non passi la porta TCP, verrà usata la porta standard 19812.
NOTA: se vuoi aprire una connessione in SSL, bisogna aggiungere ":ssl"

- nome pubblicato sulla rete del database 4D
Sintassi: 4D:<Nome_pubblicato>
In questo caso il comando apre una connessione diretta al 4D Server con il nome indicato. La porta TCP del server deve essere impostata allo stesso modo in entrambi i server.

- il nome di una data source ODBC
Sintassi: ODBC:<UserDSN> or <SystemDSN>
E' possibile indicare il nome di una sorgente dati impostata con l'amministratore ODBC.
Il prefisso non è obbligatorio per compatibilità con le versioni precedenti, ma è consigliato.

- stringa vuota
Sintassi: ""
Richiede la visualizzazione di una finestra di dialogo dove è possibile utilizzare vari pannelli per scegliere la connessione

- costante SQL_INTERNAL
Syntax: SQL_INTERNAL
In questo caso, il comando redireziona le richieste SQL al database interno.
Comandi Comandi [v12] Il terzo parametro del comando String
Quando converti una data in testo, si usa il comando stringa che accetta come secondo parametro una delle seguenti costanti:

Blank if null date ritorna "" invece di 0
Date RFC 1123 Mon, 24 Jan 2011 23:00:00 GMT
Internal date abbreviated Jan 25, 2011
Internal date long January 25, 2011
Internal date short 25/01/2011
Internal date short special 25/01/11 (ma anche 25/01/1811)
ISO Date 2011-01-25T00:00:00
ISO Date GMT 2011-01-24T23:00:00Z
System date abbreviated 25/gen/2011
System date long martedì 25 gennaio 2011
System date short 25/01/11

E il terzo parametro? Serve per le costanti di tipo RFC o ISO per aggiungere anche l'ora; ad esempio:

String(Current date;Date RFC 1123;Current time)
Tue, 25 Jan 2011 07:07:12 GMT

String(Current date;ISO Date;Current time)
2011-01-25T08:07:12
Tecniche Tecniche Aggiungere l'icona nel Dock del Mac
Ecco un metodo per aggiungere l'icona del bundle dell'app nel Doc di Mac, ripreso dalla kb di 4d ( http://kb.4d.com/search/assetid=44532 ).

In pratica usa launch external process per modificare le impostazioni dell'applicazione Dock e poi la rilancia forzando la chiusura del processo.

` ---------------------------------------------------
` Project method: AddToDock
` Example: AddToDock("myApp";"/Applications/myApp.app")
` ---------------------------------------------------
` Programmer: Jeremy Sullivan
` Created: Mon, Mar 13, 2006 8:00 AM
` ---------------------------------------------------
` Description: Aggiunge un'applicazione al Dock di Mac OS X
` ---------------------------------------------------
` Parameters
` Passed:
` $1 TEXT - Il nome dell'applicazione come dovrebbe comparire nel Dock
` $2 TEXT - Percorso in formato Posix al bundle (inclusa l'estensione .app)
` ---------------------------------------------------

C_TEXT($1;$2;$applicationName_t;$applicationPath_t)
C_TEXT($command_t;$inputStream_t;$outputStream_t;$errorStream_t)

$applicationName_t:=$1 ` miaApp
$applicationPath_t:=$2 ` percorso posix, ad esempio: /Applications/miaApp.app

$command_t:="defaults write com.apple.dock persistent-apps -array-add "
$command_t:=$command_t+"'tile-data"
$command_t:=$command_t+"file-data"
$command_t:=$command_t+"_CFURLString"
$command_t:=$command_t+""+$applicationPath_t+""
$command_t:=$command_t+"_CFURLStringType0"
$command_t:=$command_t+"file-label"+$applicationName_t+""
$command_t:=$command_t+"file-type41"
$command_t:=$command_t+"tile-typefile-tile'"

LAUNCH EXTERNAL
PROCESS($command_t;$inputStream_t;$outputStream_t;$errorStream_t)
LAUNCH EXTERNAL PROCESS("killall -HUP Dock";$inputStream_t;$outputStream_t;$errorStream_t) ` rilancia il Dock

  
Comandi Comandi Percorso in formato Posix
Per avere il percorso della cartella dove si trova il database in formato Unix (Posix) si può usare il comando GET 4D FOLDER con questa sintassi, disponibile dalla versione 4D 2004.5:

Database Folder Unix Syntax

  Il risultato sarà del tipo:

/Users/Documents/MioDatabase/


Questa caratteristica è utile soprattutto usando LAUNCH EXTERNAL PROCESS, che su Mac OS X accetta un percorso Unix (Posix).
Comandi Comandi [v12] Esempio di comando PHP per estrarre stringhe in un array
Nella v12 è integrato un interprete PHP v 5.3.2 che può essere utilizzato come estensione al linguaggio di 4d. E' possibile lanciare script php creati al momento, usare i comandi nativi di php, dei moduli preinstallati o installarne di altri.


La sintassi é
PHP Execute ( percorsoScript {; funzione {; risultato {; param1} {; param2 ; ... ; paramN}}} ) -> Function result

Riportiamo un paio di esempi semplice di uso del comando, con percorsoScript="" perché passiamo una funziona nativa del php, explode.

ARRAY TEXT($arrayTesto;0)
    $p1 :=","
  $p2 :="11,22,33,44,55"
  $isOk :=PHP Execute("";"explode";$arrayTesto;$p1;$p2)
  
- $arrayTesto conterrà le stringhe "11", "22", "33", etc.


Invece passando come parametro per il risultato un array numerico, sempre con la stessa stringa di input:

ARRAY LONGINT($arrayNumerico ;0)
  $p1 :=","
  $p2 :="11,22,33,44,55"
  $isOk :=PHP Execute("";"explode";$arrayNumerico;$p1;$p2)

- $arrayNumerico conterrà i numeri 11, 22, 33, etc.
Codice Codice [v12] Carattere di separazione fra cartelle
Per indicare il separatore fra cartelle nella v12 è stata introdotta una nuova costante che varia in funzione della piattaforma su cui è eseguito il codice, la Folder separator.

Quindi, il seguente codice avrà un risultato diverso su Mac e Windows:

$Percorso_T:=Get 4D folder(HTML Root Folder)
$Percorso_T:=$Percorso_T+"Immagini"+Folder separator+"Articoli"+Folder separator
$0:=Percorso_T
Info Info [v11 SQL] Le tabelle di sistema di 4D: _USER_SCHEMAS
La tabella che si occupa di regolamentare gli accessi al database via SQL.

_USER_SCHEMAS
SCHEMA_ID - INT32 - Numero
SCHEMA_NAME - VARCHAR - Nome
READ_GROUP_ID - INT32 - Numero del gruppo in sola lettura
READ_GROUP_NAME - VARCHAR - Nome del gruppo in sola lettura
READ_WRITE_GROUP_ID - INT32 - Numero del gruppo in scrittura
READ_WRITE_GROUP_NAME - VARCHAR - Nome del gruppo in scrittura
ALL_GROUP_ID - INT32 - Numero del gruppo con tutti i diritti (anche modifica della struttura)
ALL_GROUP_NAME - VARCHAR - Nome del gruppo con tutti i diritti
Info Info [v11 SQL] Le tabelle di sistema di 4D: _USER_CONSTRAINTS e _USER_CONS_COLUMNS
Ecco un'altra coppia di tabelle di sistema accessibili via SQL, con l'elenco delle relazioni, dei campi definiti come chiave primaria e delle regole di integrità referenziale (se cancello la testata cancello le righe, oppure non posso cancellare un record finché ci sono record in relazione.

_USER_CONSTRAINTS
CONSTRAINT_ID - VARCHAR - Numero identificativo della regola
CONSTRAINT_NAME - VARCHAR - Nome
CONSTRAINT_TYPE - VARCHAR - Tipo
TABLE_NAME - VARCHAR - Nome della tabella dove si trova la regola
TABLE_ID - INT64 - Numero della tabella con la regola
DELETE_RULE - VARCHAR - Regola di cancellazione con regola di integrità referenziale – CASCADE or RESTRICT
RELATED_TABLE_NAME - VARCHAR - Nome della tabella in relazione
RELATED_TABLE_ID - INT64 - Numero della tabella in relazione

Tipi di Constrain (o Regole):
P=chiave primaria o primary key
R= integrità referenziale - foreign key
4DR=relazione 4D

_USER_CONS_COLUMNS
CONSTRAINT_ID - VARCHAR - Numero identificativo della regola
CONSTRAINT_NAME - VARCHAR - Nome
TABLE_NAME - VARCHAR - Nome della tabella dove si trova la regola
TABLE_ID - INT64 - Numero della tabella con la regola
COLUMN_NAME - VARCHAR - Nome del campo con la regola
COLUMN_ID - INT64 - Numero del campo con la regola
COLUMN_POSITION - INT32 - Posizione del campo
RELATED_COLUMN_NAME - VARCHAR - Nome del campo in relazione
RELATED_COLUMN_ID - INT32 - Numero del campo in relazione

Da notare che le relazioni Molti a uno di 4d non sono la stessa cosa delle relazioni creati via SQL Primary Key e Foreign Key; nella Struttura anche dal punto di vista grafico sono rappresentate in maniera diversa.

Relazioni 4d:


Primary Key e Foreign Key SQL:
Info Info [v11 SQL] Le tabelle di sistema di 4D: _USER_INDEXES e _USER_IND_COLUMNS
La seguente coppia di tabelle accessibili da SQL contengono le informazioni sugli indici:

_USER_INDEXES
INDEX_ID - VARCHAR - Numero identificativo dell'indice
INDEX_NAME - VARCHAR - Nome (potrebbe essere vuoto)
INDEX_TYPE - INT32 - Tipo
TABLE_NAME - VARCHAR - Nome della tabella dove si trova l'indice
TABLE_ID - INT64 - Numero della tabella dove si trova l'indice
UNIQUENESS - BOOLEAN - Vero se l'indice prevede l'unicità

I Tipi di indice sono:
1 BTree / Composite
3 Cluster / Keyword
7 Auto


_USER_IND_COLUMNS
INDEX_ID - VARCHAR - Numero identificativo dell'indice
INDEX_NAME - VARCHAR - Nome (potrebbe essere vuoto)
TABLE_NAME - VARCHAR - Nome della tabella dove si trova l'indice
COLUMN_NAME - VARCHAR - Nome del campo dove si trova l'indice
COLUMN_POSITION - INT32 - Posizione del campo
TABLE_ID - INT64 - Numero della tabella
COLUMN_ID - INT64 - Numero del campo

Ecco per esempio il codice per cancellare tutti gli indici di un db:

ARRAY TEXT($identificativi;0)
Begin SQL
    select Index_Id from _User_Indexes into :$identificativi;
End SQL
For ($i;1;Size of array($identificativi))
        $id:=$identificativi{$i}
        $tablenum:=0
        $fieldnum:=0
        Begin SQL
           select table_id, column_Id from _User_Ind_Columns
           where Index_ID = :$id limit 1 into :$tablenum, :$fieldnum;
        End SQL
        If (($tablenum#0) & ($fieldnum#0))
           $puntCampo:=Field($tablenum;$fieldnum)
           DELETE INDEX($puntCampo;*)
        End if
End for
Info Info [v11 SQL] Le tabelle di sistema di 4D: _USER_COLUMNS
Questa tabella accessibile da SQL dà un elenco dei campi divisi per le tabelle del database:

_USER_COLUMNS
TABLE_NAME - VARCHAR - Nome della tabella
COLUMN_NAME - VARCHAR - Nome del campo
DATA_TYPE - INT32- Tipo del campo
DATA_LENGTH - INT32- Lunghezza del campo
NULLABLE- BOOLEAN - Vero se il campo accetta valori NULL
TABLE_ID - INT64 - Numero della Tabella
COLUMN_ID - INT64 - Numero del campo

Il campo Tipo contiene:

1 - Boolean
3 - Integer
4 - Long Integer
5 - Integer 64 Bits
6 - Real
7 - Float
8 - Date
9 - Time
10 - Alpha (DATA_LENGTH = ((dimensione del campo)*(2))+4
10 - Text (Lo distingui perché ha DATA_LENGTH = 0 )
12 - Picture
18 - BLOB
Comandi Comandi [v12] Rimuovere i tag nel rich text format
Con la v12 è possibile avere testo formattato, utilizzando nel campo testo i tag HTML.

Per rimuovere i tag è possibile usare il comando OBJECT Get plain text. Interessante ad esempio per fare una ricerca all'interno di campi con testi con all'interno i tag html.

Cercando ad esempio "Apple iPhone" potresti non trovare il campo con scritto:
"<SPAN> STYLE="font-size:13.5pt">Apple</SPAN> iPhone"

La soluzione è usare :
QUERY BY FORMULA([articoli];OBJECT Get plain text([articoli]descrizione)="@Apple iPhone@")
Tecniche Tecniche iphone, ipad o android e l'url 4DSYNC
Una delle funzioni aggiunte nella versione v12 è la gestione della replica dei dati, utile per sincronizzare più database in modo automatico a basso livello.

(cfr il comando Replicate su 4d )

La cosa interessante è che è possibile utilizzare questo comando anche sfruttando il protocollo HTTP: dovendo realizzare degli applicativi su piattaforma mobile la cosa semplifica di molto la comunicazione.

La modalità è di chiamare il nuovo URL specifico di 4d : /4DSYNC
E' possibile chiamarlo sia in lettura che in scrittura utilizzando stringhe costruite con la modalità JavaScript Objet Notation (JSON), abbastanza semplici sia da ottenere che interpretare.

La sintassi del comando è :

GET /4DSYNC/$catalog
Ritorna la lista delle tabelle per cui è attiva la replica e il numero di record che contengono.

GET /4DSYNC/$catalog/NomeTabella
Ritorna la descrizione della tabella.

GET /4DSYNC/NomeTabella/NomeCampo1{,NomeCampo2},...
Ritorna i dati dei campi indicati nella tabella.

POST /4DSYNC/NomeTabella/NomeCampo1{,NomeCampo2},...
Invia le modifiche fatte in locale al 4d.
Novita' Novita' Tempo consigliato per il Flush Cache
Mentre fino alla versione 4d 11 il tempo consigliato per la flush cache era di default 15 minuti.

Dalla versione v12 il tempo consigliato è 20 secondi, grazie alle nuove ottimizzazioni del funzionamento della cache.
Info Info [v12] Un nuovo metodo per riparare la base dati
La più recente versione di 4D ha introdotto un nuovo metodo per il ripristino di una base dati danneggiata.

Oltre alla procedura standard (da usare quando indici o record sono danneggiati) è stato introdotto un "Recover by record headers" che permette il ripristino dei dati quando ad essere danneggiata è tabella degli indirizzamenti dei record, può essere utilizzato questo sistema assai più complesso.

Il procedimento è da utilizzare solo quando gli altri metodi non sono andati a buon fine. Al termine del processo non è garantita comunque l'unicità dei record (i record potrebbero comparire due volte) e il mantenimento della proprietà "Never null" dei campi. Infatti se nella tabella è stato deselezionato "Records definitively deleted" potrebbero ricomparire dei record eliminati.

Il sistema è diverso dal vecchio "Recover by tags" perché richiede l'intervento dell'operatore in quanto alcuni passi mostrano una finestra di richiesta conferma.
Info Info Le caratteristiche di 4D che verranno rimosse in futuro
Ecco un elenco delle "Depercated features" che 4D ha segnalato come caratteristiche che verranno rimosse con le prossime versioni.

Cosa verrà rimpiazzato / Con cosa / da Quando
4D Chart (plug-in area) / SVG, webarea, PHP / Più avanti
4D Draw / SVG / (Rimosso dalla v11)
4D Open / Synchronization, WebServices, SQL Pass Thru / (Rimosso dalla v11)
PICT / Modern formats / Dipende dal Sistema
QuickDraw Patterns (form object property) / SVG, picture con “Replicated format” / Presto
Mac Resources / Usa “Resources” folder Sistema operativo
/ (Presto)
Old Looks / Usa System looks / Presto
Altura Mac2Win / Codice Windows nativo / Presto
SubTables / Usa tabelle N->1 / Più avanti
Web Contextual Mode / Usa il non-contextual mode / Presto
Non Unicode mode / Passa a Unicode / Presto
Info Info Migliori performance con Windows Server 2003 o 2008
Ecco alcune impostazioni consigliate per l'uso di 4d su Windows Server 2003 e 2008:

Windows Processor Scheduling: settato a “Programs” invece di “background processes”.

Windows Memory: settato a “Programs” invece di “System Cache”.

Windows Page File: settato a “System Managed Size” invece di “Custom Size”.

La posizione di queste impostazioni varia da sistema a sistema, ma tipicamente si possono trovare in questo modo:

Fai clic su Start, poi Run, e scrivi "sysdm.cpl".

Nella finestra System Properties, fai clic sul tab Advanced, e poi clic su Settings sotto Performance.

Le raccomandazioni si applicano sia a 4d Monoutenza che Server, sia quando viene lanciato come servizio che in modalità utente.
Comandi Comandi Aggiornare la gestione degli Eventi dei form
I seguenti comandi sono stati dichiarati obsoleti e andrebbero sostituiti dalla versione 11 in avanti:

Deactivated
Activated
In Header
In Break
In Footer
Before
During
After
Outside call

Quindi i pezzi di codice nei metodi dei form di questo tipo:

If(Activated)
        ORDER BY([Tabella];[Tabella]Campo;>)
        ` ...
End if

Deve diventare così:

If(Form event=On Activate)
        ORDER BY([Tabella];[Tabella]Campo;>)
        ` ...
End if


Questa è la lista delle costanti del comando Form Event da utilizzare al posto dei vecchi comandi:

Command Form Event
Deactivated -> On Deactivate
Activated -> On Activate
In Header -> On Header
In Break -> On Printing Break
In Footer -> On Printing Footer
After -> On Validate

I comandi Before e During sono un po' più complessi:

Command Form Event
Before -> On Load
Before -> On Display Detail
Before -> On Printing Detail
During -> On Clicked
During -> On Double Clicked
During -> On Losing Focus
During -> On Getting Focus
During -> On Menu Selected
During -> On Data Change
During -> On Close Box
During -> On Printing Detail
During -> On Open Detail
During -> On Close Detail

Ad esempio a volte si usava la coppia Before e During assieme:
(Before & During) -> On Display Detail
Codice Codice Metodo EliminaSpazi con Match Regex
Versione del metodo che elimina gli spazi dall'inizio e dalla fine di una stringa usando il comando Match Regex e due semplici regular espressions.

C_TEXT($miaStringa;$1)
C_TEXT($0;$risultato)
C_TEXT($pattern)
C_LONGINT($inizio;$dove;$lunga)
C_BOOLEAN($trovato)
$miaStringa:=$1
$inizio:=1
$foundFlag:=False

    `pattern regex per gli spazi all'inizio
    `^ -- indica l'inizio da dove cercare
    `\s+ -- search for one or more white spaces
$pattern:="^\\s+"

$trovato:=Match regex($pattern;$miaStringa;$inizio;$dove;$lunga)
If ($trovato)
$miaStringa:=Substring($miaStringa;$lunga+1)
End if
    `pattern regex per gli spazi alla fine della stringa
    `$ -- indica la fine dove cercare
$pattern:="\\s+$"

$trovato:=Match regex($pattern;$miaStringa;$inizio;$dove;$lunga)
If ($trovato)
$miaStringa:=Substring($miaStringa;$inizio;$dove-1)
End if
$0:=$miaStringa
1
Codice Codice Controllo Codice Fiscale
Questo metodo ritorna Vero se il codice fiscale passato è scritto correttamente, utilizzando il carattere di controllo finale. La procedura è un po' condensata: per l'algoritmo di controllo completo potete trovare il dettaglio sul sito del Ministero delle Finanze.

    `Metodo Controllo_CodiceFiscale
    `controlla il checksum del codice fiscale
    `Nexus srl 4-6-90
C_LONGINT($i;$n_l;$car_l;$cod_l)
C_STRING(16;$cf_s;$1)
C_STRING(43;$mysndcod_s)
$cf_s:=$1
If (Length($cf_s)#16)
  $0:=False
Else
  $n_l:=0
  For ($i;2;14;2)
    $car_l:=Ascii($cf_s[[$i]])
    Case of
      : (($car_l>=48) & ($car_l<=57))
        $cod_l:=$car_l-48
      : (($car_l>=65) & ($car_l<=90))
        $cod_l:=$car_l-65
      Else
        $cod_l:=0
    End case
    $n_l:=$n_l+$cod_l
  End for
  $mysndcod_s:="BAFHJNPRTVUMBERTOBAFHJNPRTVCESULDGIMOQKWZYX"
  For ($i;1;15;2)
    $car_l:=Ascii($cf_s[[$i]])-47
    $cod_l:=Ascii($mysndcod_s[[$car_l]])-65
    $n_l:=$n_l+$cod_l
  End for
  $0:=((65+$n_l-(Int($n_l/26)*26))=Ascii($cf_s[[16]]))
End if
1
Info Info [v11 SQL] Le tabelle di sistema di 4D: _USER_TABLES
In accordo con le specifiche SQL, 4th Dimension contiene delle tabelle di sistema che descrivono la struttura del database:
  • _USER_TABLES
  • _USER_COLUMNS
  • _USER_INDEXES
  • _USER_CONSTRAINTS
  • _USER_IND_COLUMNS
  • _USER _CONS_ COLUMNS
  • _USER_SCHEMAS
.

La prima che andiamo ad analizzare è la _USER_TABLES, che contiene la descrizione delle tabelle del database. I campi che la compongono sono:

TABLE_NAME - VARCHAR - Il nome della tabella;
TEMPORARY - BOOLEAN - Vero se la tabella è temporanea, altrimenti Falso;
TABLE_ID - INT64 - Il numero della tabella;
SCHEMA_ID - INT32 - Il numero dello schema;

Così, come segnalato da Francesco Pandolfi sul NUG italiano, è possibile controllare se una tabella con un certo nome esiste usando il codice:

C_INTEGER($cnt)
C_TEXT($tabName)
$tabName:=$1

Begin SQL
        select count(*) from _user_tables
        where table_name = :$tabName
        into :$cnt;
End SQL

If ($cnt>0)
           `esiste
Else
           `non esiste
End if
Info Info [v11 SQL] Cercare una stringa in tutta la base dati
Ecco di seguito un metodo per cercare un qualsiasi dato fra i campi alfanumerici di un database. Quando ogni dato viene trovato, restituisce un messaggio con tabella e campo in cui si trova il valore.


$quanti:=Get last table number
For ($i;1;$quanti)
  If (Is table number valid($i))
      For ($j;1;Get last field number($i))
        If (Is field number valid($i;$j))
           GET FIELD PROPERTIES($i;$j;$fieldType;$fieldLen;$indexed)
           If (($fieldType=Is Alpha Field ) | ($fieldType=Is Text ))
           $valoreSt:=$1
           $tabellaptr:=Table($i)
           $campoptr:=Field($i;$j)
           QUERY($tabellaptr->;$campoptr->=$valoreSt)
           If (Records in selection($tabellaptr->)>0)
           ALERT("["+Table name($i)+"]"+Field name($i;$j))
           End if
           End if
          End if
      End for
    End if
End for
ALERT("Finito")
Info Info [v11 SQL] Risalire al numero di tabella (o alla sua esistenza)
Col seguente codice si può chiedere a 4D se una tabella esiste:



C_TEXT($1) ` Table name
C_BOOLEAN($0)

C_LONGINT($i)

$i:=1
$0:=False
While(($i<=Get last table number)&(Not($0)))
If (Is table number valid($i))
If (Table name($i)=$1)
$0:=True
End if
End if
$i:=$i+1
End while



Una variante può permettere di sapere il numero di tabella, se non esiste restituisce -1:


C_TEXT($1) ` Table name
C_LONGINT($i;$0)

$i:=1
$0:=-1
While($i<=Get last table number)
If (Is table number valid($i))
If (Table name($i)=$1)
$0:=$i
$i:=Get last table number+1
End if
End if
$i:=$i+1
End while
Info Info [v11 SQL] Dove si trovano i manuali?
Dalla versione v11 i manuali non vengono più installati con le applicazioni.

Questo è l'indirizzo dove trovare i manuali di 4d in inglese:
http://www.4d.com/support/documentation.html

Per chi vuole consultare la guida online, l'indirizzo è:
http://www.4d.com/docs/4DDOCUS.HTM

Per configurare il percorso dell'help richiamabile con il tasto F1 nelle preferenze va scritto questo:
http://www.4d.com/docs/

2
Tecniche Tecniche Un pulsante con più scorciatoie per la tastiera
Può capitare di dover associare a un pulsante più scorciatoie da tastiera: il caso più classico è un pulsante a cui è associato il pulsante "Carriage return" al quale si vuole associare anche il pulsante "Enter".

Ovviamente una possibilità è quella di duplicare varie volte il pulsante (con pulsanti nascosti), e a ogni copia assegnare la scorciatoia desiderata: l'inconveniente è che la modifica del codice di un pulsante costringe a modificare (o duplicare nuovamente) tutti gli altri.

Soluzione più agevole è quella di far invece "puntare" tutti i pulsanti copia al pulsante principale. Nell'esempio visto prima, nell'On clicked del metodo del pulsante associato ad "Enter" ci sarà scritto:

POST KEY(Carriage return)

Info Info [v11 SQL] Le query sui campi NULL e Map NULL values to blank values
La particolarità di un campo NULL sta proprio nel suo valore "speciale". Per tale motivo, se viene eseguita una query cercando una stringa vuota, i campi alpha/text contenenti il valore NULL non saranno presi in considerazione: questo significa che per trovare tali record è necessario scrivere del codice aggiuntivo.
Per evitare tali problematiche è possibile attivare nelle proprietà del campo l'opzione Map NULL values to blank values; questa, infatti, agisce a un livello molto basso del database, poiché assimila i valori NULL ai valori di default (stringa vuota, data 00/00/00, ecc.) del relativo campo.
Questa opzione è sempre attiva in automatico per ogni campo.
Info Info [v11 SQL] I valori di default per i NULL
Quando si gestiscono dei valori NULL, 4D esegue in automatico delle conversioni quando questi valori vengono usati.
Se viene infatti eseguita l'istruzione:

miaAlphavar:=[table1]MioAlphafield

la variabile miaAlphavar prenderà il valore "".
In generale, le conversioni a tali valori di default vengono eseguite secondo questo schema:

• Per Alpha e Text: ""
• Per Real, Integer e Long Integer: 0
• Per Date: 00/00/00
• Per Time: 00:00:00
• Per Boolean: False
• Per Picture: immagine vuota
• Per Blob: blob vuoto
Comandi Comandi [v11 SQL] I valori NULL nei campi
Con la versione 11 e l'introduzione del motore SQL, anche con 4D è possibile gestire i valori NULL. Un esempio di campo NULL è un booleano che contiene i valori Maschio/Femmina quando si censisce un'anagrafica: finché l'utente non setta il valore corretto il valore del campo è NULL.

Per impostare il valore di un campo a NULL si usa il comando SET FIELD VALUE NULL, mentre per sapere se il valore del campo è NULL si usa il comando Is field value Null.
Plugin Plugin Quando gli SMTP_Attachment vengono scambiati per virus
Utilizzando due diversi server SMTP di Telecom Italia (un ADSL standard e un Interbusiness) per inviare dei pdf ad un servizio di fax server, su uno dei due capitava che il messaggio consegnato non fosse il pdf inviato, bensì un fax che recitava: "Il file è stato rimosso perché potenzialmente pericoloso".
Dopo varie richieste ai tecnici Telecom si è trovata la causa: l'attach veniva inviato come UUEncode utilizzando

SMTP_Attachment ( smtp_ID ; fileName ; encodeType ; deleteOption )

dove encodeType valeva 7.
Ora, il file veniva codificato al momento dell'esecuzione del comando SMTP_Send. Ciò però comportava che la dimensione comunicata (del file di partenza) e quella reale (del file codificato) risultassero diverse, facendo credere al server SMTP che al file allegato fosse stato agganciato del codice malevolo.

La soluzione è stata però agevole: è bastato infatti utilizzare il comando

IT_Encode ( fileName ; encodedFile ; encodedMode )

utilizzando come encodedMode sempre 7, UUEncode.
A questo punto basta eseguire SMTP_Attachment come prima ma usando come file da allegare encodedFile e come encodeType il valore -7, che per 4D significa che il file allegato è già codificato come UUEncode.

Non variando a questo punto la dimensione dell'allegato, il file viene correttamente inviato.
Tecniche Tecniche Selezioni ordinate con CREATE SELECTION FROM ARRAY
CREATE SELECTION FROM ARRAY permette di creare selezioni ordinate di record.

Supponiamo di voler utilizzare dei record in un ordine ben preciso (per esempio i clienti in ordine di fatturato, dove il fatturato deve essere calcolato "al volo").

Una soluzione è popolare un array con i record number dei clienti e il loro fatturato, ordinare i due array secondo quello col fatturato poi usare. Ecco un codice di esempio:

SELECTION TO ARRAY([Tabella];$arrayRecordNumer;[Tabella]campoX;$arrayX;Tabella]campoY;$arrayY)
ARRAY REAL($arrayFatturato;size of array($arrayRecordNumer))

`... elaboro i campi X e Y per ottenere il fatturato che vado a scrivere nell'iesimo elemento del nuovo array arrayFatturato

SORT ARRAY($arrayFatturato;$arrayRecordNumer)
CREATE SELECTION FROM ARRAY ([Tabella]; $arrayRecordNumer; "")

Con l'ultimo comando creo la selezione ordinata.
Comandi Comandi [v11 SQL] Dimensione dei parametri testo in plugin esterni come Stmp_body
Benchè le variabili testo di 4D abbiano superato il limite di 32K ci sono ancora dei comandi che hanno parametri testo che richiedono questo limite. Uno di questi è SMTP_Boby che richiede che il teso passato sia ancora limitato a 32K.
Se usate delle righe di codice come le seguenti per creare il corpo della email.

  $offset:=0
  While ($offset<BLOB size(Mail_Blob))
    $body:=BLOB to text(Mail_Blob;Text without length ;$offset)
    $noError:=(ErrCheck ("SMTP_Body";SMTP_Body ($Smtp_id;$body;2)))
  End while

Ricordatevi che lo stesso metodo nella v11 to text deve diventare cosi:

  $body:=BLOB to text(Mail_Blob;Text without length ;$offset;$maxsize)
  $offset:=0
  $maxsize:=30000
  While ($offset<BLOB size(Mail_Blob))
    $body:=BLOB to text(Mail_Blob;UTF8 Mac Text without length ;$offset;$maxsize))
    $noError:=(ErrCheck ("SMTP_Body";SMTP_Body ($Smtp_id;$body;2)))
  End while

dove $maxsize deve essere inferiore o uguale a 32K
Info Info Avviare lo spegnimento automatico del PC Windows da 4D
E' possibile spegnere automaticamente il computer utilizzando il comando LAUNCH EXTERNAL PROCESS.
Il comando deve essere eseguito nel "On Exit" (se serve distinguere fra versioni monoutente, client o server, e in caso usare "On Server Shutdown") così:

LAUNCH EXTERNAL PROCESS("shutdown -s -t 100";$tIn;$tOut)

attendendo 100 secondi prima di spegnere la macchina (un tempo ragionevole per pensare che 4D sia uscito). Se si aggiunge il parametro -f:

LAUNCH EXTERNAL PROCESS("shutdown -s -t 100 -f";$tIn;$tOut)

Windows forzerà la chiusura dei processi.


Se mentre il sistema si sta chiudendo ci si ricorda che c'era qualcos'altro da fare, nel database method "On Sturtup" si può inserire:

LAUNCH EXTERNAL PROCESS("shutdown -a";$tIn;$tOut)

per annullare un precedente spegnimento avviato.
Info Info [v11 SQL] Display Format e il punto e la virgola
Il formato è la stringa di testo del tipo "###.###,00" con cui puoi modificare la modalità di visualizzazione di un numero (c'è anche per data, ora, booleano)

Mentre fino alla versione 2004 nel formato il punto e la virgola rispecchiavano le impostazioni del sistema corrente, nella versione v11 (finalmente!) il punto sta sempre per la posizione dei decimali e la virgola per il separatore delle migliaia.

Cioé rende il programma facilmente utilizzabile anche in quegli ambienti dove si usano impostazioni 'americane' pur trovandosi in italia, ad esempio.

Questa impostazione è di default per le strutture create con la v11, mentre per quelle convertite dalle versioni precedenti esiste l'opzione Use period and comma as placeholders in numeric formats in Preferenze - Design Compatibility.
Novita' Novita' [v11 SQL] Cast dei dati con 4D ODBC Pro
La v11 SQL non consente più il cast in automatico dei dati con i comandi di ODBC.

Per essere più chiari, se cerco di importare in un array di tipo stringa dei dati che nella sorgente ODBC sono invece di tipo longint, l'array sarà riempito da stringhe vuote; a differenza della versione 2004, dove il cambio di tipo era automatico ( e peraltro abbastanza pericoloso per l'integrità dei dati, benché comodo per il programmatore! )
Info Info [v11 SQL] Il form deve esistere!
Se nella v11SQL viene eseguito il codice

OUTPUT FORM([Tabella];"Output")

e il form non esiste, 4D restituisce errore, a differenza della versione 2004, dove il form poteva anche non esistere.
Codice Codice [v11 SQL] Esportazione dati
Ecco una versione modificata per la v11 di un metodo di esportazione dati in un file testo.

C_TEXT($testo)
C_BLOB($blob)
$pointertable:=->[LaTabella]
For ($j;1;Get last field number($pointertable))
  If (Is field number valid(Table($pointertable);$j))
    $testo:=$testo+Field name(Table($pointertable);$j)+Char(Tab )
    If ($j      $testo:=$testo+Char(Tab )
    End if
  End if
End for
$testo:=$testo+Char(Carriage return )+Char(Line feed )
TEXT TO BLOB($testo;$blob)

ALL RECORDS($pointertable->)
ARRAY LONGINT($arrRecNum;0)
SELECTION TO ARRAY($pointertable->;$arrRecNum)
For ($i;1;Size of array($arrRecNum))
  GOTO RECORD($pointertable->;$arrRecNum{$i})
  $testo:=""
  For ($j;1;Get last field number($pointertable))
    If (Is field number valid(Table($pointertable);$j))
      $pointer:=Field(Table($pointertable);$j)
      If ((Type($pointer->)=Is Alpha Field ) | ((Type($pointer->)=Is Text )))
        $testo:=$testo+$pointer->
      Else
         $testo:=$testo+String($pointer->)
      End if
      If ($j<Get last field number($pointertable))
        $testo:=$testo+Char(Tab )
      End if
    End if
  End for
  $testo:=$testo+Char(Carriage return )+Char(Line feed )
  TEXT TO BLOB($testo;$blob;UTF8 text without length ;*)
End for
BLOB TO DOCUMENT("Export.txt";$blob)
Info Info Ordinamento dei campi booleani
Sarà pure una cosa scontata, ma ordinando un campo booleano in maniera crescente vengono visualizzati prima i valori "False" e poi i "True"
Tecniche Tecniche Selezionare un record in una List Box basata su Named Selection
Mettiamo di avere una List Box legata ad una Named Selection : se voglio far in modo che 4D mi selezioni un record in particolare (ad esempio il primo) dopo un ordinamento, devo utilizzare il set dichiarato in Highligth Set e fare questo simpatico giro di set :


QUERY([Tabella];[Tabella]campoChiave=[TabellaMadre]chiave)
ORDER BY([Tabella];[Tabella]campoOrdinamento;<)
COPY NAMED SELECTION([Tabella];"miaLista") 'Set usato per la ListBox

$num:=Record number([Tabella])
GOTO RECORD([Tabella];$num)
CREATE SET([Tabella];"il_Primo")
COPY SET("il_Primo";"selezionati") 'Set dichiarato come highligth set
Info Info [v12 SQL] Nuove funzionalità in Stampa
Due grosse novità:

1) si può stampare la listbox, anche su più pagine


2) posizionamento per coordinate degli oggetti in fase di stampa

La sequenza da fare è:

- OPEN PRINTING JOB
- seleziono una maschera con OPEN PRINTING FORM
- stampo il singolo oggetto con Print Object

Info Info [v12 SQL] Nuovo Code Editor
Ecco alcune delle novità del nuovo Code Editor:
  • uso del // per il commento
  • zoom sul testo
  • autocompletamento dei comandi Sql, delle tabelle e campi anche in sql
  • autocompletamento delle variabili locali, proposte automaticamente nelle dichiarazioni
  • il Selection to array (fino a 128 parametri) può essere splittato su più righe con il backslash
  • si possono impostare bookmark con il ctrl-tasto di funzione
  • evidenziazione delle modifiche in giallo, in verde quando sono salvate
  • il breakpoint segue la riga dove è stato messo
  • modalità di correzione delle maiuscole delle variabili (ad es in camelCase
  • collapse di codice selezionato, tag con anteprima
Info Info [v11 SQL] Le opzioni del file 4DLINK
Il file .4DLINK è il nuovo tipo che di file che consente di aprire i database 4D.
In linea generale sostituisce il file .PTH che consentiva di aprire un 4D Client con le opzioni utente memorizzate, ma in realtà contiene un numero molto maggiore di opzioni.

Il file si trova nella cartella:

• Windows Vista: C:\Users\UserName\AppData\Roaming\4D\Favorites v11\
• Windows XP: C:\Documents and Settings\UserName\Application
Data\4D\Favorites v11\
• Mac OS: Users/UserName/Library/Preferences/4D/Favorites v11/

dove UserName è il nome dell'utente del sistema.

Il file 4DLINK è un file XML che nella sua forma più semplice si presenta così:

<?xml version="1.0" encoding="UTF-8"?>
<database_shortcut structure_file="file:///C:/mio.4DB"
data_file="file:///C:/mio.4DD"/>

All'interno del file è possibile inserire tutta una serie di attributi, come ad esempio nome utente e passoword:

<?xml version="1.0" encoding="UTF-8" standalone="no" ?>
<database_shortcut is_remote="true" password="123"
server_database_name="Contabilità" server_path="contabilita.dominio.it"
user_name="Steve"/>

Novita' Novita' [v12 SQL] XLIFF: dalle applicazioni multilingua alle finestre multilingua
Uno degli aspetti introdotti dalla v11 SQL era stata la possibilità di sviluppare software multilingua grazie all'uso di XLIFF.

Con 4D v12 SQL viene data innanzitutto la possibilità di utilizzare una lingua diversa da quella impostata nel sistema operativo; ma soprattutto sarà possibile definire, all'interno di ogni processo, se deve essere utilizzato per quel processo un fie XLIFF differente, senza che ciò intacchi l'impostazione di base della lingua del database intero o degli altri processi attivi.
Info Info Cos'è il documento .Match ?
Dalla versione 11.5 è presente un documento vicino alla struttura chiamato nome_database.Match .

Il nuovo file è creato per collegare i numeri delle tabelle e il loro corrispondente UUID; è richiesto per ripristinare un database danneggiato. Non cancellarlo, è aggiornato ogni volta che c'è una modifica alla struttura.

E' ovviamente il caso di aggiungere questo documento alla lista dei file che saranno automaticamente salvati durante il backup automatico.

Info Info [v12 SQL] PHP Execute
La novità che ha richiesto il maggior tempo di sviluppo nella v12 SQL è la possibilità di utilizzare php all'interno di 4D col comando il cui nome provvisorio è PHP Execute.

4D integra (a momento) il motore PHP versione 5.3, ma è possibile anche utilizzare anche un interprete PHP esterno.

Molte delle librerie standard sono già incluse e già utilizzabili (l'elenco delle librerie built-in sarà descritto sul manuale).

Questo permette di passare da sistemi MAMP (Mac, Apache, MySQL e PHP) o WAMP (Windows, Apache, MySQL e PHP) a sistemi M4DP o W4D, in cui 4D evita l'installazione e manutenzione di tre componenti.
Tecniche Tecniche Accedere ad un database 4D
"io ho la necessità di collegarmi ad un database 4d (che gira su piattaforma Mac) da un server MSSQL per estrarre e pubblicare alcuni dati.
Potete spiegarmi la procedura per poter effettuare questa operazione?"

Risponde Umberto Migliore:
Ci sono diverse possibilità:
  • se sul server 4d è presente la licenza web si può preparare una chiamata in GET o POST che ritorni i valori in un formato elaborabile (tipo server REST)
  • se è presente la licenza WebService si può fare una chiamata SOAP usando l'indirizzo automatico http://indirizzoserver/4DWSDL
  • se si vuole accedere alle tabelle direttamente si può usare una chiamata ODBC dopo aver installato il corrispondente driver ODBC (disponibile gratuitamente)
  • via PHP utilizzando il driver PDO_4D incluso nelle ultime release di PHP


Diciamo che dipende dal tipo di informazioni che si vogliono passare, con che frequenza e con che controllo di validità o se servono eventuali elaborazioni a monte.
8
Novita' Novita' [v12 SQL] Listbox gerarchiche
Le listbox ottengono una nuova funzionalità: quando alcune colonne hanno dei valori ripetuti è possibile scegliere una visualizzazione gerarchica, dove è possibile espandere e collassare le righe raggruppate sotto le stesse voci.
Novita' Novita' [v12 SQL] Oggetti dinamici nei form
Con la v12 SQL sarà possible costruire dinamicamente i form.

Dinamiche saranno anche le variabili: sarà possibile infatti avere variabili solo col nome la cui visibilità e utilizzo diventa non il processo chiamante bensì il form in quanto tale.

Uno dei comandi più potenti presentati è il comando di Duplicazione degli Oggetti, che dà la possibilità di aggiungere in fase di utilizzo campi ed etichette.

Ovviamente il linguaggio di programmazione mette a disposizione tutti gli strumenti per poter interagire con questo tipo di oggetti, tramite puntatori: sarà possibile ad esempio cambiare il testo delle etichette.. come dire che sono dinamici anche gli oggetti statici!
Novita' Novita' [v12 SQL] Rich text: il testo con formattazione
Con 4D v12 SQL sarà possibile avere un oggetto testo formattato.

Può essere personalizzata dimensione, forma, colore e quant'altro. Il testo è formattabile se si attiva un apposito check nelle proprietà dell'oggetto che lo visualizza a video.

Due nuovi comandi del linguaggio permettono inoltre di ottenere il testo nella versione con i tag html che esplicita gli stili oppure lo stesso testo nella versione pulita dagli stili.
Codice Codice Conversione da esadecimale a decimale
Passando come parametro una stringa in forma esadecimale al seguente metodo, questo restituisce il valore decimale relativo:

C_TEXT($CifraEsadecimale_t;$tutteLeCifre_t;$1;$NumEsadecimale_t)
C_LONGINT($0;$posizione_l)
$0:=0
$tutteLeCifre_t:="0123456789ABCDEF"
$NumEsadecimale_t:=$1
While (Length($NumEsadecimale_t)>0)
          $CifraEsadecimale_t:=Substring($NumEsadecimale_t;0;1)
          $NumEsadecimale_t:=Substring($NumEsadecimale_t;2;Length($NumEsadecimale_t))
          $0:=$0*16
          $posizione_l:=Position($CifraEsadecimale_t;$tutteLeCifre_t)-1
          $0:=$0+$posizione_l
End while



2
Info Info [v11 SQL] Il Record number in un trigger
Quando viene eseguito il trigger per l'evento On saving new record, se si usa il comando Record number la v11 SQL restituisce il record number assegnato al record, a differenza della 2004 che restituiva come record number "-3".
Plugin Plugin Ottenere il proprio indirizzo IP con due schede di rete installate
Abbiamo già visto in una precedente faq come ottenere il proprio indirizzo ip.

Non è detto però che questo metodo funzioni quando sulla macchina sono installate due schede di rete (tipo ethernet e wireless).

Un interessante approccio al problema è stato proposto da Jeffrey Kain della Orchard Software Corporation; la sua soluzione è:

$Err:=NET_Resolve (Current machine;$ip)

Il comando è fra quelli disponibili nel plugin gratuito 4d Internet Commands.
Web Web Dichiarazione delle variabili per i metodi web services
Quando si decide di rendere fruibile un metodo via web service server è bene creare manualmente il metodo COMPILER_WEB.

Questo metodo (che a differenza degli altri metodi COMPILER non viene creato automaticamente) si occupa in sostanza di dichiarare le variabili che vengono passate come parametro al metodo chiamato dal client web service.

Ciò risulta obbligatorio quando il parametro passato come SOAP input è un array: in tal caso infatti nel metodo COMPILER_WEB l'array viene dichiarato con dimensione 0, ma nel metodo chiamato come web service l'array avrà la dimensione corretta in funzione degli elementi che aveva sul client web.
Plugin Plugin Impostare i parametri di stampa in 4D Write
Per impostare i parametri di stampa in 4D Write si utilizza la funzione WR SET PRINT OPTION.
Gli unici comandi del linguaggio standard che vengono utilizzati in fase di stampa da 4DWrite sono PRINT OPTION VALUES, per conoscere i parametri da passare a WR SET PRINT OPTION in alcune circostanze specifiche (wr paper option, wr paper source option) e SET CURRENT PRINTER per impostare la stampante da usare.
Per conoscere il valore delle impostazioni di stampa di 4D Write si usa la funzione WR GET PRINT OPTION.
Info Info [v11 SQL] Oggetti come espressioni
Una delle funzioni della v11 che facilmente può sfuggire è il fatto che agli oggetti oltre ad assegnare una variabile è possibile assegnare un'espressione.

Cosa significa? Ad esempio, in una lista l'oggetto potrebbe al posto della variabile contenere [tabella]cognome+" "+[tabella]nome per andare a sostituire i due campi relativi.

Oppure, sempre in una lista l'oggetto potrebbe essere un termometro e si può scrivere qualcosa del genere [tabella]quantità/vTotale".

Per cose più complesse, del tipo in funzione di un valore numerico cambiare una descrizione, si può accoppiare con il comando Choose, anch'esso probabilmente sfuggito ai più; esempio:
Choose(Day number([tabella]giorno);"Dom";"Lun";"Mar";"Mer";"Gio";"Ven";"Sab").
Info Info [v11 SQL] Modifiche al comando QUERY BY FORMULA
Con la v11 SQL i comandi QUERY BY FORMULA e QUERY SELECTION BY FORMULA richiedono obbligatoriamente come parametro la tabella su cui eseguire la query.

Come già detto, per migliorare le prestazioni questi comandi vengono eseguiti sul Server quando vengono eseguiti da un Client. Inoltre è stato implementato un sistema di ottimizzazione delle QUERY BY FORMULA per migliorarne ulteriormente le prestazioni.
1
Novita' Novita' [v11 SQL] Il comando Find in field
Il comando Find in field della v11 SQL sostituisce il Find index key delle versioni precedenti.

Il comando permette di trovare il numero di record che contengono un certo valore da cercare senza modificare la selezioni corrente.

La differenza più sostanziale, oltre al nome, sta nel fatto che il comando adesso accetta anche campi non indicizzati per la ricerca.
Novita' Novita' [v11 SQL] Modifiche al comando QUERY WITH ARRAY
Rispetto alle versioni precedenti, con la v11 SQL non è più necessario che il campo su cui eseguire il comando sia indicizzato.
Info Info Tagliare un testo in più righe e metterlo in array
Se ho un testo molto lungo e voglio tagliarlo su più righe di massimo 80 caratteri, ma tagliate dove c'è un separatore tipo spazio o punteggiatura, si può usare questo codice:

$Pat_T:="(?:[^\\p{C}]{1,72})(?:$|\\p{Z})"
ARRAY TEXT($Fold_aT;0)
$Start_L:=1
$FoundAt_L:=0
$Length_L:=0
$Bool_B:=Match regex($Pat_T;$Buf_T;$Start_L;$FoundAt_L;$Length_L)
While ($Bool_B)
  APPEND TO ARRAY($Fold_aT;Substring($Buf_T;$FoundAt_L;$Length_L))
  $Start_L:=$FoundAt_L+$Length_L
  $Bool_B:=Match regex($Pat_T;$Buf_T;$Start_L;$FoundAt_L;$Length_L)
End while

cfr Use Match regex to fold or split text into strings of a maximum length
Codice Codice Come usare le transazioni nelle maschere di inserimento V2
Un altro schema per l'uso delle transazioni nell'inserimento o nella modifica di un record:

Metodo del Form di inserimento


Case of
  : (Form Event=On Load)
    START TRANSACTION
    ....

  : (Form Event=On Validate)
    VALIDATE TRANSACTION
    ....

  : (Form Event=On Unload)
    If (In transaction)
      CANCEL TRANSACTION
    End if

End case

Da ricordare che il form event On Unload di default non è attivo per il form e dunque è da attivare manualmente.
1
Info Info [v11 SQL] Problema nell'aggiornamento dei campi Picture
Prima della disponibilità dei campi di tipo Blob era usuale registrare nel campo Picture anche dati che non fossero immagini.

Però registrando delle immagini 4D accodava 6 byte con la posizione e la modalità di visualizzazione dell'immagine: durante l'aggiornamento della base dati alla v11, 4D elimina questi 6 byte in fondo ai dati registrati nei campi Picture.

Nel caso in cui non si tratta di immagini questo costituisce sicuramente un problema: per risolverlo basta cambiare il tipo del campo da Picture a Blob.
Codice Codice Come usare le transazioni nelle maschere di inserimento
Schema di base per l'utilizzo delle transazioni nella creazione o modifica di un record.

Metodo del Form di inserimento

Case of
  : (Form Event=On Load)
    START TRANSACTION
    ....



Pulsante Annulla

    Case of
           : (Form Event=On Clicked)
           CANCEL TRANSACTION
           CANCEL
      End case



Pulsante Conferma

Case of
  : (Form Event=On Clicked)
    ...
    If ($hoFattoTuttiIControlli)
      SAVE RECORD
      VALIDATE TRANSACTION
    Else
      CANCEL TRANSACTION
    End if
    CANCEL
End case
Info Info [v11 SQL] Usare più CALL WEB SERVICE in un'unica sessione
Dalla versione v11 è stato aggiunto un ulteriore parametro al comando CALL WEB SERVICE; la sintassi adesso è:

CALL WEB SERVICE (accessURL; soapAction; methodName; namespace{; complexType{; *}})

L'* finale può essere usato per ottimizzare le chiamate. Quando viene passato, il comando non chiude la connessione usata dal processo alla fine dell'esecuzione. In questo modo la successiva chiamata CALL WEB SERVICE riutilizzera la stessa connessione, finché verrà usato l'* finale.

Il meccanismo accelererà in modo notevole le sequenze di chiamata allo stesso server, soprattutto via Internet, sempre che il Web Server chiamato abbia attivo il "keep-alive".

Se il server non ha l'opzione attiva o se si raggiunge il limite massimo di chiamate nella connessione, 4d si occuperà automaticamente di riaprire una nuova connessione quando serve.
Tecniche Tecniche Lanciare una stored in 4d 2004 *
Nella versione 11 è possibile impostare un flag per cui un metodo verrà lanciato in automatico sul server (dove in alcune condizioni le cose sono molto più veloci); il processo resterà in attesa del metodo e ne userà il risultato.

Per ottenere la stessa cosa in 2004 dobbiamo fare in modo che il client attenda la fine del processo sul server per leggere la risposta e la stored invece invece alla fine dell'esecuzione deve prima di chiudersi aspettare che il client abbia la risposta.
Ecco un metodo che si può usare come base di lavoro:

=== SUL CLIENT:
C_BLOB(blob_risposta)
$id:=Execute on server("metodo_suserver";128*1024;"metodo")
nexus_stored("attendi";$id)
  ` ... qui posso usare la risposta



=== SUL SERVER:
nexus_stored("start")
  ` ... qui va il tuo codice che scrive la risposta in un blob, ad esempio
nexus_stored("stop")



=== ECCO IL METODO BASE:
    ` Method nexus_stored
    ` Nexus srl, www.nexusonline.it

Case of
  : ($1="start")  ` === startup del processo
    If (Application type=4D Server )
      C_BOOLEAN(stored_finito;client_letto)
      stored_finito:=False
      client_letto:=False
        C_BLOB(blob_risposta)
        SET BLOB SIZE(blob_risposta;0)
    End if
    
  : ($1="stop")  ` === chiusura processo
    If (Application type=4D Server )
      client_letto:=False
      stored_finito:=True
      $start:=Milliseconds
      While (Not(client_letto) & ((Milliseconds-$start)<30000))
        DELAY PROCESS(Current process;1)
      End while
    End if
    
  : ($1="attendi")  ` === legge la risposta dalla stored e la chiude
    C_BOOLEAN(stored_finito;processo_finito;client_letto)
    C_TEXT(currentProgressStatus)
    $pid:=$2
    processo_finito:=False
    Repeat
      DELAY PROCESS(Current process;10)
      GET PROCESS VARIABLE($pid;stored_finito;processo_finito)
    Until (processo_finito)
    GET PROCESS VARIABLE($pid; blob_risposta; blob_risposta)
    client_letto:=True
    SET PROCESS VARIABLE($pid;client_letto;client_letto)
End case
1
Info Info Come ri-generare il file Catalog.xml
Come già citato, il file Catalog.xml permette di mantenere durante la conversione da 2004 a v11 gli stessi codic identificativi usati nella base dati.

Il file è generato automaticamente durante la conversione e andrebbe salvato per poter riutilizzare.. Nel caso si fosse perso, per rigenerarlo basta:

- Andare in Design nella struttura già convertita in v11
- Scegliere dal menù File il comando "Export Structure to xml"
- Rinominare il file così creato in Catalog.xml

Per utilizzarlo basta metterlo allo stesso livello della struttura in 2004 da convertire.
Tecniche Tecniche Diamo una mano al compilatore
Il compilatore di 4D in certi casi ha bisogno di un aiuto per essere guidato nei meandri delle varie opzioni di alcuni comandi. Vediamo un esempio:

Il comando Table può ricevere come parametro sia un puntatore che un numero, restituendo rispettivamente o un numero o un puntatore: ciò significa che se uso

SET FIELD RELATION (Table($x)....)

in modo interprete funziona, ma quando si prova a compilare restituisce errore, perché il compilatore non sa a priori cosa restituisce Table($x).

Come risolvere?

Basterà usare una variabile ben dichiarata e tutto scorre liscio:

$tabella_l:=Table(->[MiaTabella])
SET FIELD RELATION ($tabella_l;.....)

Comandi Comandi Modificare lo stato di una relazione: SET FIELD RELATION
Per modificare lo stato di una relazione è assai utile il comando SET FIELD RELATION, soprattutto quando una relazione non è definita automatica da struttura e invece si vuole che lo diventi temporaneamente. La sintassi del comando è:

SET FIELD RELATION (manyTable | manyField; one; many)

dove
- manyTable è la tabella e in questo caso la modifica varrà per tutte le relazioni che partono dalla tabella;
- oppure manyField è il campo da cui parte la relazione;
- one e many prendono il valore delle seguenti costanti per definire le relazioni uno e molti:
Do not modify (0) non modifica nulla;
Structure configuration (1) prende per la relazione il valore stabilito nella struttura;
Manual (2) rende la relazione manuale;
Automatic (3) la rende automatica.


Un esempio:

SET AUTOMATIC RELATIONS(False;False) `Reset delle relazioni
`Solo le relazioni definite adesso verranno usate
SET FIELD RELATION([Invoices]Cust_IDt;Automatic;Automatic)
SET FIELD RELATION([Invoice_Row]Invoice_ID;Automatic;Automatic)
QR REPORT([Invoices];Char(1);True;True;True)
Codice Codice PDO_4D: il codice PHP per l'inserimento di dati
Ecco di seguito un esempio di codice per PDO_4D che consente l'uso di PHP per l'inserimento di dati, con creazione della tabella.


<?php
$dsn = '4D:host=localhost;charset=UTF-8';
$user = 'test';
$pass = 'test';

// Connection to the 4D SQL server
$db = new PDO_4D($dsn, $user, $pass);

try {
        $db->exec('CREATE TABLE test(id varCHAR(1) NOT NULL, val VARCHAR(10))');
} catch (PDOException $e) {
        die("Errore 4D : " . $e->getMessage());
}
          
$db->exec("INSERT INTO test VALUES('A', 'A')");
$db->exec("INSERT INTO test VALUES('B', 'A')");
$db->exec("INSERT INTO test VALUES('C', 'C')");

$stmt = $db->prepare('SELECT id, val from test');

$stmt->execute();
print_r($stmt->fetchAll());

unset($stmt);
unset($db);
?>




L'ouput risultante sarà:


Array
(
        [0] => Array
           (
           [ID] => A
           [0] => A
           [VAL] => B
           [1] => B
           )

        [1] => Array
           (
           [ID] => C
           [0] => C
           [VAL] => D
           [1] => D
           )

        [2] => Array
           (
           [ID] => E
           [0] => E
           [VAL] => F
           [1] => F
           )

)
Info Info Esportazione Descrizione Struttura
Esporta in un file di testo con la descrizione della struttura corrente; include il numero di record, le relazioni con indicazioni se automatiche Molti a Uno o Uno a Molti.

C_LONGINT($TabellaNumero;$tabella;$campo;$lunghezza;$tipo)

$cr:=Char(Carriage return )
$doc:=Create document("")
If (ok=1)
  GET TABLE TITLES($Tabelle_nomi;$Tabelle_numeri)
  SORT ARRAY($Tabelle_nomi;$Tabelle_numeri)
  For ($tabella;1;Size of array($Tabelle_nomi))
    $TabellaNome:=$Tabelle_nomi{$tabella}
    $TabellaNumero:=$Tabelle_numeri{$tabella}
    SEND PACKET($doc;$cr+"TABLE "+$TabellaNome+" ("+String(Records in table(Table($TabellaNumero)->))+")"+$cr)
    For ($campo;1;Count fields($TabellaNumero))
      GET FIELD PROPERTIES($TabellaNumero;$campo;$tipo;$lunghezza)
      Case of
        : (($tipo=Is LongInt ) | ($tipo=Is Integer ))
          $tipo:="Long"
        : ($tipo=Is Real )
          $tipo:="Real"
        : ($tipo=Is Alpha Field )
          $tipo:="Alfa "+String($lunghezza)
        : ($tipo=Is Text )
          $tipo:="Text"
        : ($tipo=Is Boolean )
          $tipo:="Bool"
        : ($tipo=Is Date )
          $tipo:="Date"
        : ($tipo=Is Time )
          $tipo:="Time"
        : ($tipo=Is Picture )
          $tipo:="Pict"
        Else
          $tipo:="blob "+String($tipo)
      End case
      GET RELATION PROPERTIES($TabellaNumero;$campo;$oneT;$oneF;$cf;$autoOne;$autoMany)
      If ($oneT=0)
        $relazione:=""
      Else
        $relazione:="-> ["+Table name($oneT)+"]"+Field name($oneT;$oneF)+(Num($autoOne)*"A1")+" "+(Num($autoOne)*"AM")
      End if
      SEND PACKET($doc;Char(Tab )+$tipo+Char(Tab )+Field name($TabellaNumero;$campo)+Char(Tab )+$relazione+$cr)
      
    End for
  End for
  CLOSE DOCUMENT($doc)
  ALERT("finito")
End if
Info Info [v11 SQL] Il comando Select Rgb Color
Usa questo comando per mostrare la finestra standard del sistema operativo di scelta del colore.



Un primo parametro serve a preimpostare un colore nella finestra (il default è il nero), mentre il secondo permette di indicare una stringa come titolo (il default è "Colors")

Comandi Comandi [v11 SQL] Il comando TRUNCATE TABLE
Il comando TRUNCATE TABLE, ereditato evidentemente dall'SQL Truncate (Table), permette la cancellazione di tutti i record di una tabella in una singola operazione.

Rispetto alla consueta coppia di ALL RECORDS / DELETE SELECTION questo comando è molto più veloce, non esegue i trigger né controlla le relazioni per eventuali integrità referenziali, non aggiorna neanche il LockedSet se trova dei record bloccati.
Semplicemente ritorna ok = 1 se ha avuto effetto, altrimenti si interrompe con ok = 0.

Utile ad esempio per ripulire velocemente una tabella temporanea.
Comandi Comandi [v11 SQL] Il comando Transform Picture
Rispetto alle versioni precedenti, adesso oltre agli operatori (+,/,*..) è possibile modificare un'immagine con il comando Transform Picture.

TRANSFORM PICTURE($immagine;Scale;$larghezza;$altezza)
I parametri $larghezza e $altezza sono il decimale del fattore di scala, ad esempio per dimezzare al 50% si usa lo 0,5

TRANSFORM PICTURE($immagine;Translate;$oriz;$vert)
I parametri sono numero anche negativo di pixel di spostamento.

TRANSFORM PICTURE($immagine;Flip horizontally)
Per ottenere un'immagine speculare orizzontalmente.

TRANSFORM PICTURE($immagine;Flip vertically)
Per ottenere un'immagine speculare verticalmente.

TRANSFORM PICTURE($immagine;Reset)
Per annullare tutte le modifiche di sopra.

Invece, non sono annullabili le seguenti opzioni:

TRANSFORM PICTURE($immagine;Fade to grey scale)
Trasforma l'immagine in scala di grigi.

TRANSFORM PICTURE($immagine;Crop;$x;$y;$larg;$alt;)
Ritaglia l'immagine partendo dalle coordinate x e y per un certo numero di pixel di larghezze e altezza
Comandi Comandi [v11 SQL] Il comando Get 4D Folder e i nuovi parametri
Al comando Get 4D Folder, già presentato in una una precedente faq, accetta nuovi parametri molto utili per identificare alcune cartelle di lavoro. Ecco l'elenco completo ad oggi:

  • Active 4D Folder
  • Licenses Folder
  • Extras Folder (dalla v11.2 da non usare, meglio la Resource folder)
  • 4D Client Database Folder
  • Database Folder
  • Database Folder Unix Syntax
    (utile su Mac quando si usa il comando Launch external process)
  • Current Resources folder
  • Logs Folder
  • HTML Root Folder



Info Info 4d avviato come servizio su Windows Vista e Windows Server 2008
A partire da Windows Vista e quindi su tutte le versioni successive di Windows (ad esempio Windows Server 2008) non è possibile interagire con un applicativo lanciato in modalità servizio (cfr. Service Changes for Windows Vista )

La soluzione (da 4d v11 in poi) è lanciare il Client sulla stessa macchina e usare la finestra di Amministrazione per controllare il server.

Oppure per non usare 4d come servizio è possibile fare in modo che sia lanciato all'avvio in modalità normale seguendo queste istruzioni:

1. impostare il login automatico di un utente

2. creare un file autostart.4DLink
<?xml version="1.0" encoding="UTF-8"?><database_shortcut structure_file="file:///C:/4D/miaApp.4DB" data_file="file:///C:/4D/miaApp.4DD"/>

3. creare un file .bat che lanci il 4d Server e il relativo .4DLink
"C:\4D v11 SQL Release 4 Custom\4D Server\4D Server.exe" "C:\autostart.4Dlink"
Plugin Plugin [v11 SQL] Modifiche apportate a WR Get text di 4D Write
Fino alla versione 2004 WR Get text ha ritornato un numero massimo di caratteri pari a 32.000 (vedi ad esempio )
Dalla versione 11, se il database gira in modalità Unicode, il limite è diventato lo stesso delle variabili testo, cioè 2GB.
Codice Codice Confrontare il contenuto di due dischi o due cartelle
Avendo la necessità di controllare se una copia di sicurezza di un disco fosse andata a buon fine, ho scritto questo metodo ricorsivo che confronta il contenuto di due differenti percorsi. È scritto con la v11SQL. Ecco il metodo ConfrontoDischi:


If (Count parameters=0)
    $percorso1:=Select folder("Percorso 1")
    If (OK=1)
        $percorso2:=Select folder("Percorso 2")
        If (OK=1)
           ARRAY TEXT(arrFile1;0)
           ARRAY TEXT(arrFile2;0)
           ConfrontoDischi (->arrFile1;$percorso1)
           ConfrontoDischi (->arrFile2;$percorso2)
           For ($i;Size of array(arrFile1);1;-1)
           arrFile1{$i}:=Delete string(arrFile1{$i};1;Length($percorso1))
           End for
           For ($i;Size of array(arrFile2);1;-1)
           arrFile2{$i}:=Delete string(arrFile2{$i};1;Length($percorso2))
           End for
           For ($i;Size of array(arrFile1);1;-1)
           $find:=Find in array(arrFile2;arrFile1{$i})
           If ($find>0)
           DELETE FROM ARRAY(arrFile1;$i)
           DELETE FROM ARRAY(arrFile2;$find)
           End if
           End for
        End if
  End if
Else
    $percorso:=$2
    ARRAY STRING(255;$arrNomiFile;0)
    DOCUMENT LIST($percorso;$arrNomiFile)
    For ($i;1;Size of array($arrNomiFile))
        APPEND TO ARRAY($1->;$percorso+$arrNomiFile{$i})
    End for
    ARRAY STRING(255;$arrNomiFile;0)
    FOLDER LIST($percorso;$arrNomiFile)
    For ($i;1;Size of array($arrNomiFile))
        ConfrontoDischi ($1;$percorso+$arrNomiFile{$i}+"\\")
    End for
End if
Codice Codice Esportazione dati in formato testo
Ecco un metodo per esportare i dati di una tabella "Tabella" in formato solo testo, con conversione del set di caratteri in formato Windows:


$vhDocRef:=Open Document("")
If (ok=1)
  $tableptr:=->[Tabella]
  $tablenum:=Table($tableptr->)

  `prima creo la testata con i nomi dei campi
  For ($j;1;Count fields($tableptr))
    GET FIELD PROPERTIES($tablenum;$j;$fieldType;$fieldLength;$indexed;$unique;$invisible)
    $nome:=Field name($tablenum;$j)
    If ($invisible=False)
      SEND PACKET($vhDocRef;Mac to Win($nome))
      SEND PACKET($vhDocRef;Char(Tab))
    End if
  End for
  SEND PACKET($vhDocRef;Char(Carriage return))

  `seleziono tutti i record
  ALL RECORDS($tableptr->)
  ARRAY LONGINT($arrRecNum;0)
  SELECTION TO ARRAY($tableptr->;$arrRecNum)

  `esporto tutto, tranne i campi Invisibili
  For ($i;1;Size of array($arrRecNum))
    GOTO RECORD($tableptr->;$arrRecNum{$i})
    $rigadaesportare:=""
    For ($j;1;Count fields($tableptr))
      GET FIELD PROPERTIES($tablenum;$j;$fieldType;$fieldLength;$indexed;$unique;$invisible)
      If ($invisible=False)
        $pointer:=Field($tablenum;$j)
        $testodaesportare:=""
        Case of
          : (($fieldType=Is Alpha Field ) | ($fieldType=Is Text ))
           $testodaesportare:=$pointer->
           $testodaesportare:=Replace string($testodaesportare; Char(Carriage return);" ")
          : ($fieldType=Is Boolean )
           If ($pointer->)
           $testodaesportare:="Vero"
           Else
           $testodaesportare:="Falso"
           End if
          Else
           $testodaesportare:=String($pointer->)
        End case

        $rigadaesportare:=$rigadaesportare+Mac to Win($testodaesportare)+Char(Tab)
      End if
    End for
    SEND PACKET($vhDocRef;$rigadaesportare;Char(Carriage return))
  End for
  CLOSE DOCUMENT($vhDocRef)
End if
Info Info [v11 SQL] Scambio dati fra applicazioni 4d
Con l'ultima versione di 4D non è più disponibile 4d Open, molto utilizzato per scambiare i dati fra diversi server 4d.

Le opzioni disponibili oggi sono:
  1. SQL Pass-trough, cioè diretto senza passare da driver odbc
  2. Web Services, utilizzando il protocollo SOAP
  3. HTTP, scambiando file XML


In linea generale il consiglio di 4d è di usare nell'ordine

Sincronizzazione -> SQL, XML, SOAP

Divisione di operatività -> SOAP, SQL, XML

Applicazioni distribuite -> SQL, SOAP, XML

Per maggiori informazioni, potete scaricare la White Paper 4D Data Exchange v11 nella sezione delle Brochure 4D
Info Info Messaggio "PR4D Resource Damaged"
Quando si danneggia un indice della struttura 4d v11.x mostra una finestra con un messaggio "PR4D resource damaged and no previous version available!".

Gli indici della struttura sono conservati nel file .4dIndy, quindi per risolvere il rpoblema basta eliminare il file e farglielo ricostruire al prossimo riavvio di 4d.
Info Info Vedere un'immagine SVG in tempo reale
Durante la creazione di un'immagine SVG, è molto importante per gli sviluppatori avere la possibilità di vedere velocemente l'immagine che stanno creando.

E' possibile salvare l'xml in una variabile o esportarlo in un file esterno (e poi visualizzarla usando ad esempio un browser moderno); oppure 4D mette a disposizione una finestra SGV Viewer dove vedere il risultato al volo.

Per vedere questa finestra bisogna chiamare il comando SVGTool_SHOW_IN_VIEWER del componente 4D SVG fornito con 4d SQL v11.
Plugin Plugin 4D Chart: centrare verticalmente un grafico con CT SET REAL SCALE
Vediamo come centrare un grafico ottenuto tramite CT Chart arrays.
Ciclando sull'array dei valori troviamo il massimo valore assoluto nell'array. Sia $max questo massimo.
Possiamo quindi scrivere:

$intervallo:=$max/2
CT SET REAL SCALE (Area;$Chart;0;0;0;0;-$max;$max;$intervallo;0,1)

La variabile $intervallo e il valore 0,1 variano ovviamente in base ai valori che si attendono nel grafico.
Info Info Il comando Length e il carattere \
Supponiamo di fare il seguente assegnamento:

$stringa:="\"a\""

La variabile $stringa ha un comportamento al quale è necessario fare attenzione: se la si usa per fare un confronto o un assegnamento il suo valore sarà «"a"», ma il valore di Length($stringa) è 3 (perché formata da 3 caratteri) e non 5 come si potrebbe supporre.
Plugin Plugin Controllare il protocollo di acquisizione di QPix
La funzione QPx_AcqTestProtocol consente la possibilità di utilizzare un certo protocollo di acquisizione fra quelli supportati dal plugin della Escape.
L'uso è:

$error:=QPx_AcqTestProtocol(acqProtocol)

dove acqProtocol può prendere i valori:

qpx_AcqTWAINProtocol che ha valore 1 per il protocollo TWAIN
qpx_AcqPictureTransferProtocol che ha valore 2 per il PTP (Picture Transfer Protocol identifier)
qpx_AcqVideoProtocol che ha valore 4 per l'acquisizione Video.

Se il protocollo è supportato viene ritornata la costante qpx_NoErr, altrimenti, in base all'errore, qpx_couldntGetRequiredComponent o qpx_paramErr.
Plugin Plugin L'elenco delle periferiche di scansione con QPix
La funzione

$err:=QPx_AcqGetDeviceList (acqProtocol;arrListaPeriferiche)

permette di conoscere l'elenco delle periferiche utilizzabili col protocollo definito da acqProtocol. I protocolli possibili sono:

  • TWAIN Protocol: per il protocollo TWAIN.
  • Picture Transfer Protocol: per la cattura immagine su MacOS X e e l'acquisizione immagine su Windows col protocollo PTP.
  • Video Protocol: per l'acquisizione video con QuickTime MacOS X e DirectShow su Windows.


Info Info [v11 SQL] Metodo generico "On sql authentication"
Dalla versione 11.3 è stato implementato lo schema di sicurezza per l'accesso ai dati da SQL language; l'autenticazione è fatta nel metodo del database "On Sql Authentication" che però richiede necessariamente di eseguire il comando CHANGE CURRENT USER.

Il metodo seguente valida nome e password dalla lista degli utenti standard di 4d; in realtà si potrebbe creare un utente generico "utente_sql" e usare quello nel comando successivo.

C_TEXT($1;$2;$3)
C_BOOLEAN($0)

$nomeutente:=$1
$password:=$2
$0:=False

GET USER LIST($atListaUtenti;$alListaID)
$quale:=Find in array($atListaUtenti;$nomeutente)
If ($quale>0)
  If (Validate password($alListaID{$quale};$password)
    CHANGE CURRENT USER($nomeutente;$password)
    $0:=True
  End if
End if

Novizi Novizi I quattro stati dei pulsanti nelle picture della tool box
È possibile creare all'interno della picture library delle icone a 4 stati, le icone cioè cambiano in funzione dello stato del pulsante relativo. Dall'alto in basso i quattro stati rappresentati sono:

  • pulsante cliccabile
  • pulsante sotto fuoco (on focus)
  • pulsante cliccato
  • pulsante non cliccabile



Codice Codice Un termometro con incremento corretto
Quando creiamo in un form un termometro, 4D inserisce in automatico i valori di intervallo da 0 a 100. Ecco un metodo per utilizzare questo termometro (vTerm nel nostro caso) con qualsiasi numero (positivo o negativo, intero o decimale) all'interno di un ciclo.

$start:=0,65
$end:=0,90
$progress:=0,001
For ($i;$start;$end;$progress)
      vTerm:=100*($i-$start+$progress)/($end-$start+$progress)
      DISPLAY RECORD
End for


dove $start è il valore da cui il ciclo parte, $end è il valore di arrivo del ciclo, $progress è lo step di avanzamento.

Info Info Autoincremento automatico di un campo numerico
Per i campi numerici interi (Integer, Long Integer e
Integer 64 bits) è possibile utilizzare un nuovo (dalla v11.2) flag "Autoincrement" che genera automaticamente un ID unico nei vari record. Il campo non è più modificabile o inseribile né dall'utente né da programma.

Il numero è sempre unico, mai riutilizzato, e se generato in una transazione poi annullata viene perso.

Da notare che se viene attivato su una tabella che abbia già dei record il numero parte dal numero totale dei record +1; in questo modo prima di applicarlo alla struttura è possibile rinumerare i record precedenti per avere una sequenza corretta.
Codice Codice Il numero di giorno nell'anno
Ecco una semplice formula con la quale calcolare il numero di giorno all'interno dell'anno:

$giornodellanno=Current date-Add to date(!00/00/00!;Year of(Current date);1;1)+1
Info Info [v11 SQL] Scorciatoia per avere info su oggetti nelle maschere
In ambiente User (che nella v11 è integrato con l'ambiente Design ) premendo control-maiuscolo quando il puntatore si trova su un oggetto campo o variabile, appare un tip con alcune informazioni:
  • il nome dell'oggetto
  • il posizionamento fisico (alto, sinistra, larghezza, altezza)
  • il nome della variabile o il numero di tabella e di campo
File File Metodo Benchmark v1.2.1*
Contiene una struttura dove propongo una base di benchmark delle versioni di 4D e delle piattaforme su cui viene lanciato.

In questo momento il codice è abbastanza approssimativo e non pesa allo stesso modo le varie parti del test (Export e Import sono sicuramente più significative delle altre funzioni in questo modo), inoltre testa solo il db engine del 4d mono, quindi create record, indexing, query, sort.

    `Method Benchmark
    `version 1.2.2 - 5 feb 2009
DEFAULT TABLE([Table])
If (Records in table>0)
  ALERT("You should launch this one on a new db")
  ALL RECORDS
  DELETE SELECTION
  FLUSH BUFFERS
Else
  $quantity:=Num(Request("How many record?";String(1000)))
  If (ok=1)
    $start:=Tickcount
    $firstStart:=$start
    `create record
    For ($i;1;$quantity)
      CREATE RECORD
      [Table]First:=String($i)
      For ($j;1;10)  `tre lettere
        [Table]Second:=[Table]Second+Char((Random%(122-97+1))+97)
      End for
      [Table]Numeric:=Random*Random
      SAVE RECORD
    End for
    FLUSH BUFFERS
    $benchmark_create:=Tickcount-$start
    $start:=Tickcount
    For ($i;1;$quantity)
      MESSAGE(String($i)+" on "+String($quantity))
      CREATE RECORD
      [Table]First:=String($i)
      For ($j;1;10)
        [Table]Second:=[Table]Second+Char((Random%(122-97+1))+97)
      End for
      [Table]Numeric:=Random*Random
      SAVE RECORD
    End for
    FLUSH BUFFERS
    $benchmark_createWithDialog:=Tickcount-$start
    $start:=Tickcount
    ALL RECORDS([Table])
    EXPORT TEXT([Table];"testexport.txt")
    $benchmark_export:=Tickcount-$start
    $start:=Tickcount
    IMPORT TEXT([Table];"testexport.txt")
    FLUSH BUFFERS
    $benchmark_import:=Tickcount-$start
    $start:=Tickcount
    SET INDEX([Table]First;True)
    $benchmark_index:=Tickcount-$start
    $start:=Tickcount
    QUERY([Table];[Table]First>"4")
    $benchmark_queryMedium:=Tickcount-$start
    $start:=Tickcount
    QUERY([Table];[Table]First<"9")
    $benchmark_queryLarge:=Tickcount-$start
    $start:=Tickcount
    QUERY([Table];[Table]First<"51";*)
    QUERY([Table]; & ;[Table]Second>"M")
    $benchmark_queryDouble:=Tickcount-$start
    $start:=Tickcount
    ORDER BY([Table];[Table]First;>)
    $benchmark_orderSingle:=Tickcount-$start
    $start:=Tickcount
    ORDER BY([Table];[Table]Second;>;[Table]First;<)
    $benchmark_orderDouble:=Tickcount-$start

    $benchmark_global:=Tickcount-$firstStart

    If (Compiled application)
      $compiled:=" Comp"
    Else
      $compiled:=" Inter"
    End if
    $result:="GLOBAL mm:ss:tt ["+String($quantity)+$compiled+"] "
    $result:=$result+Time string($benchmark_global/60)+", "+String($benchmark_global)+"tick"+Char(13)+Char(Line feed )
    ALERT($result)
    $result:=$result+"create "+Time string($benchmark_create/60)+", "+String($benchmark_create)+"tick"+Char(13)+Char(Line feed )
    $result:=$result+"withDialog "+Time string($benchmark_createWithDialog/60)+", "+String($benchmark_createWithDialog)+"tick"+Char(13)+Char(Line feed )
    $result:=$result+"export "+Time string($benchmark_export/60)+", "+String($benchmark_export)+"tick"+Char(13)+Char(Line feed )
    $result:=$result+"import "+Time string($benchmark_import/60)+", "+String($benchmark_import)+"tick"+Char(13)+Char(Line feed )
    $result:=$result+"index "+Time string($benchmark_index/60)+", "+String($benchmark_index)+"tick"+Char(13)+Char(Line feed )
    $result:=$result+"qryMedium "+Time string($benchmark_queryMedium/60)+", "+String($benchmark_queryMedium)+"tick"+Char(13)+Char(Line feed )
    $result:=$result+"qryLarge "+Time string($benchmark_queryLarge/60)+", "+String($benchmark_queryLarge)+"tick"+Char(13)+Char(Line feed )
    $result:=$result+"qryDouble "+Time string($benchmark_queryDouble/60)+", "+String($benchmark_queryDouble)+"tick"+Char(13)+Char(Line feed )
    $result:=$result+"orderSingle "+Time string($benchmark_orderSingle/60)+", "+String($benchmark_orderSingle)+"tick"+Char(13)+Char(Line feed )
    $result:=$result+"orderDouble "+Time string($benchmark_orderDouble/60)+", "+String($benchmark_orderDouble)+"tick"+Char(13)+Char(Line feed )
    SET TEXT TO CLIPBOARD($result)
    C_BLOB($BLOB)
    TEXT TO BLOB($result;$BLOB;3)
    $vhDocRef:=Create document("";"txt")  ` Save the document of your choice
    If (OK=1)  ` If a document has been created
      CLOSE DOCUMENT($vhDocRef)  ` We don't need to keep it open
      BLOB TO DOCUMENT(Document;$BLOB)
      ` Write the document contents
    End if
  End if
End if
1
Tecniche Tecniche Importare un campo boolean da un file di testo
Facendo un import di dati from file con un campo booleano ho provato sia con true (prima lettera minuscola) che con 1 ma alla fine ho scoperto che era sufficiente scrivere True con la prima lettera maiuscola.
Versione 2004 7.
1
Web Web Accedere a un sito su una macchina locale - Apple MacOSX
Dopo l'esempio per Windows, vediamo come modificare il file hosts su Mac per rendere accessibile da rete interna un sito web come se ci trovassimo all'esterno della rete stessa.

Individuare il file:
/etc/hosts

e inserire la seguente riga:
192.168.1.1 www.sviluppo4d.it

dove l'ip è quello interno del web server.

Individuare poi il file:
/etc/host.conf

che si occupa del lookup e che deve contenere la riga:
order hosts,bind

Infine, nel file:
/etc/nsswitch.conf

inserire la riga:
hosts: files nisplus nis dns
Web Web Accedere a un sito su una macchina locale - Microsoft Windows
Per poter consultare un sito installato all'interno della propria rete come se ci trovassimo all'esterno è necessario modificare il file "hosts" della nostra macchina. Vediamo la soluzione per PC.

Individuare il file hosts nei percorsi seguenti:

Windows 95/98/Me c:\windows\hosts
Windows NT/2000/XP Pro c:\winnt\system32\drivers\etc\hosts
Windows XP Home c:\windows\system32\drivers\etc\hosts

e inserire la seguente riga:

192.168.1.1 www.sviluppo4d.it

dove l'ip è quello interno del web server.
Info Info Anteprima di stampa in un Quick Report
Per avere l'anteprima di stampa di un rapporto di statistica creato con i comandi di Quick Report, è necessario aggiungere il comando:

QR SET DOCUMENT PROPERTY($ID;1;1)

dove $ID è l'id dell'area Quick report, i due "1" rappresentano la proprietà "qr printing dialog" e la richiesta di mostrare la dialog stessa
Codice Codice [v11 SQL] Caricare una lista in un array
Il comando LIST TO ARRAY non è più consigliato dalla versione v11, probabilmente nelle versioni successive potrebbe essere reso non disponibile.

Ecco un semplice codice che permette di caricare in un array il contenuto di una lista:

$lista_hl:=Load list("Posizione_fiscale")

ARRAY TEXT(Lista;Count list items($lista_hl))

For ($i; 1; Count list items($lista_hl))
  GET LIST ITEM($lista_hl; $i; $rifer_l; $nome_t)
  Lista{$i}:=$nome_t
End for
Bug Bug [Risolto] Crash su Windows Vista all'apertura di 4D
Sembra che su Windows Vista, in alcune circostanze non meglio identificate, 4D 2004.7 vada in crash appena aperto.

In questi casi la soluzione è tanto semplice quanto incredibile: basta spostare i software (Runtime/Server/Client e Struttura/Dati) fuori dalla cartella "Programmi" di sistema.
Info Info 4D v11 SQL e Joomla: problemi con Unicode UTF8
Dovendo implementare una comunicazione fra un sito sviluppato con Joomla come CMS e dei dati memorizzati su un database 4D v11 SQL è possibile che i caratteri Unicode inviati da 4D al sito (tipo le lettere con accenti) non vengano visualizzati correttamente, malgrado il database mySQL interfacciato con Joomla sia UTF8 e nell'installazione la scelta della lingua sia stata italiano.

Per correggere il problema:
- entrare nella cartella "language" presente nella root di installazione di Joomla
- all'interno di ogni cartella riferita ad uno specifico linguaggio ("it-IT", "en-GB", ecc.) individuare il file "linguadiinterfaccia.xml", dove linguadiinterfaccia è una stringa identica al nome della cartella che lo contiene.
- individuare la riga "winCodePage" e sotituire il set "iso-8859-1" con "UTF-8"

A questo punto i caratteri speciali, tipo le lettere accentate, sono visualizzati correttamente.
Web Web Creare un sito per Apple iPhone - Riconoscere il browser Safari
Se si desidera creare un versione del sito ottimizzato per iPhone è importante intanto riconoscere che il navigante sia su un iPhone.
Siccome ogni browser è riconoscibile tramite il suo User Agent (un testo che lo identifica univocamente), se il collegamento avviene tramite iPhone lo User Agent sarà simile (ovviamente cambieranno i numeri di versione nel tempo):

Mozilla/5.0 (iPhone; U; CPU like Mac OS X; en) AppleWebKit/420.1 (KHTML, like Gecko) Version/3.0 Mobile/4A93 Safari/419.3


che è diverso dallo User Agent del Safari installato ad esempio su un Apple MacOSX 10.5 o su un Microsoft Windows XP, e perfino da quello installato su un iPod Touch!

Quindi nel codice della On Web Connection si potrà scrivere così, presupponendo di avere la versione delle pagine per iphone in un'apposita cartella:

wbUrl:=$1
If ((position("iPhone";wbUrl)>0) | (position("iPod";wbUrl)>0))
  percorso_s:="iphone/"
Else
  percorso_s:=""
End if

`.. qui la gestione normale

SEND HTML FILE(percorso_s+"pagina.html")
Info Info Architettura del sito Sviluppo4d.it
Ecco una breve descrizione su com'è fatto Sviluppo4d.it:

Il software è un programma scritto in 4d SQL v11.

L'applicazione è una Monoutenza con la licenza Web Application Server; la versione non commerciale ha un costo di 106 Euro, ma questa ci è stata fornita da 4d con l'intercessione di Italsoftware (grazie a Dominique e Massimo!)

Le pagine web vengono servite e costruite direttamente da 4D e sono una decina di template in html e cinque include per testata, piede, colonne sinistra e destra e striscia di navigazione.

Le pagine robots.txt e sitemap.xml sono interamente dinamiche.

L'hardware è un MacMini G4 1.42GHz con Mac OS X.

Per un aggiornamento di quale versione di 4d è attiva usate l'url speciale 4dwebtest .
Codice Codice Filtro caratteri da non utilizzare in un URL
`web_urlEncode
  `prepara una stringa in modo che sia utilizzabile come parte di un Url

C_TEXT($0;$1)
C_LONGINT($car_n;$ascii_n)
C_STRING(31;$car_s;$nuovo_s)

$0:=""
$1:=Mac to ISO($1)
For ($car_n;1;Length($1))
  $car_s:=Substring($1;$car_n;1)
  $ascii_n:=Character code($car_s)

  Case of
    : ($ascii_n =32)
      $nuovo_s:="+"
    : ($ascii_n >=127)
      $nuovo_s:="%"+Substring(String($ascii_n;"&$");2)
    : (Position($car_s;":<>&%= \"")>0)
      $nuovo_s:="%"+Substring(String($ascii_n;"&$");2)
    Else
      $nuovo_s:=Char($ascii_n)
  End case
  $0:=$0+$nuovo_s
End for

Info Info Filtro caratteri da non utilizzare in un XML
Nel caso in cui preparate un file in formato XML elaborandolo in formato testo (e quindi non utilizzando i comandi XML 4d) occorre verificare che i testi da utilizzare siano formattati in modo da evitare alcuni caratteri speciali.

  `metodo web_xmlEncode

$stringa:=$1

$risultato:=Replace string($stringa;"&";"&amp;")
$risultato:=Replace string($risultato;"'";"&apos;")
$risultato:=Replace string($risultato;"\"";"&quot;")
$risultato:=Replace string($risultato;">";"&gt;")
$risultato:=Replace string($risultato;"<";"&lt;")

$0:=$risultato
Codice Codice Generazione del file Sitemap.xml con i comandi XML DOM di 4d
Stessa cosa dell'esportazione già citata in formato test (vedi la faq Generazione automatica del file Sitemap.xml), ma scritta utilizzando i comandi Xml.

    `metodo web_sitemap.xml

C_STRING(16;vXML)
C_STRING(80;$aNSName1;$aNSName2;$aNSValue1;$aNSValue2)
C_TEXT($result)

$site:="http://www.sviluppo4d.it"

    `valori della struttura xml richiesta
$Root:="urlset"
$xpath_url:="url"
$xpath_loc:="loc"

$Namespace:="http://www.google.com/schemas/sitemap/0.84"
$aNSName1:="xmlns:xsi"
$aNSValue1:="http://www.w3.org/2001/XMLSchema-instance"
$aNSName2:="xsi:schemaLocation"
$aNSValue2:="http://www.google.com/schemas/sitemap/0.84 http://www.google.com/schemas/sitemap/0.84/sitemap.xsd"

    `creo il documento xml in memoria
vXML:=DOM Create XML Ref($Root;$Namespace;$aNSName1;$aNSValue1;$aNSName2;$aNSValue2)

    `prima elenchiamo gli indirizzi fissi

$url:=DOM Create XML element(vXML;$xpath_url)
$item:=DOM Create XML element($url;$xpath_loc)
DOM SET XML ELEMENT VALUE($item;$site)  `indirizzo base del sito

$url:=DOM Create XML element(vXML;$xpath_url)
$item:=DOM Create XML element($url;$xpath_loc)
DOM SET XML ELEMENT VALUE($item;$site+"/Users")  `un indirizzo fisso su sviluppo4d.it

    `poi costruiamo l'elenco degli indirizzi dinamici
ALL RECORDS([News])
While (Not(End selection([News])))
  $txt:=$site+"/Detail_News_Display?id="+String([News]id)+"&title="+(wb_UrlEncode ([News]Title))
  $url:=DOM Create XML element(vXML;$xpath_url)
  $item:=DOM Create XML element($url;$xpath_loc)
  DOM SET XML ELEMENT VALUE($item;$txt)
  NEXT RECORD([News])
End while

ALL RECORDS([Faq])
While (Not(End selection([Faq])))
  $txt:=$site+"/Detail_FAQ_Display?id="+String([Faq]id)+"&title="+(wb_UrlEncode ([Faq]Title))
  $url:=DOM Create XML element(vXML;$xpath_url)
  $item:=DOM Create XML element($url;$xpath_loc)
  DOM SET XML ELEMENT VALUE($item;$txt)
  NEXT RECORD([Faq])
End while

    `i tag sono chiusi automaticamente, esportiamo il documento xml
DOM EXPORT TO VAR(vXML;$result)

    `adesso lo cancelliamo dalla memoria
DOM CLOSE XML(vXML)

SEND HTML TEXT($result)

Info Info Modifica intestazione ListBox
Volendo modificare il titolo di una colonna di una listbox, supponendo che il titolo si chiami "Header1", basta inserire questo codice nel form method:

Case of
  : (Form event=On Load )
    BUTTON TEXT(Header1;"ID Telematizzazione accise")
    BUTTON TEXT(Header2;"Codice Deposito commerciale")
End case

e così via.
Codice Codice Generazione automatica del file Sitemap.xml
Se avete un sito dinamico, dove non tutti i link sono facilmente raggiungibili dai motori di ricerca, è utile comunicare agli stessi l'elenco degli indirizzi disponibili con un protocollo chiamato sitemap.

L'indicazione dell'esistenza della sitemap viene comunicata al motore di ricerca o tramite una loro pagina dedicata (come Google) oppure più genericamente con una riga nel file robots.txt (vedi ad esempio la faq Generazione automatica del file Robots.txt dove è descritto il file robots di Sviluppo4d.it).

Clicca qui per vedere il risultato.

`metodo web_sitemap

$eol:="\r\n"
$txt:=""

$txt:=$txt+"<?xml version='1.0' encoding='UTF-8'?>"+$eol
$txt:=$txt+"<urlset xmlns=\"http://www.google.com/schemas/sitemap/0.84\" "
$txt:=$txt+"xmlns:xsi=\"http://www.w3.org/2001/XMLSchema-instance\" "
$txt:=$txt+"xsi:schemaLocation=\"http://www.google.com/schemas/sitemap/0.84 "
$txt:=$txt+"http://www.google.com/schemas/sitemap/0.84/sitemap.xsd\">"+$eol

  `preparo la parte iniziale e finale del singolo url
$s:="<url><loc>http://www.sviluppo4d.it" `qua va il nome del sito corrente
$e:="</loc></url>"+$eol

`prima elenchiamo gli indirizzi fissi
$txt:=$txt+$s+""+$e`indirizzo base del sito
$txt:=$txt+$s+wb_xmlEncode ("/Users")+$e`indirizzo fisso su sviluppo4d.it

`poi costruiamo l'elenco degli indirizzi dinamici
ALL RECORDS([News])
While (Not(End selection([News])))
  $txt:=$txt+$s+"/Detail_News_Display?id="+String([News]id)+"&title="+ wb_xmlEncode( wb_urlEncode ([News]Title))+$e
  NEXT RECORD([News])
End while
ALL RECORDS([Faq])
While (Not(End selection([Faq])))
  $txt:=$txt+$s+"/Detail_FAQ_Display?id="+String([Faq]id)+"&title="+ wb_xmlEncode( wb_urlEncode([Faq]Title))+$e
  NEXT RECORD([Faq])
End while

  `chiudiamo il tag principale
$txt:=$txt+"</urlset>"
SEND HTML TEXT($txt)
1
Codice Codice Generazione automatica del file Robots.txt
In alcune faq precedenti (Scrivere i robots per i siti dinamici in 4d 1, Scrivere i robots per i siti dinamici in 4d 2 e Scrivere i robots per i siti dinamici in 4d 3) avevamo descritto la metodologia di scrittura del metatag nello header per ovviare al problema della sessione inclusa nei link delle pagine dinamiche.

Con la nuova versione di sviluppo4d, adesso aggiornata in v11, quella strategia non è più necessaria.

Il metodo seguente si occupa della generazione dinamica del file "robots.txt" che viene richiamato dai motori di ricerca.
In questa forma essere facilmente modificato e aggiornato anche da programma.

Clicca qui per vedere il risultato.

`metodo web_robots.txt

$eol:="\r\n"
$txt:=""
    `per tutti i motori di ricerca
$txt:=$txt+"User-Agent: *"+$eol
    `questi link non sono da seguire
$txt:=$txt+"Disallow: /Login"+$eol
$txt:=$txt+"Disallow: /User.new"+$eol
$txt:=$txt+"Disallow: /Comment.add"+$eol
$txt:=$txt+"Disallow: /Detail_FAQ_New"+$eol
    `mentre tutto il resto si
$txt:=$txt+"Allow: /"+$eol
    `e segnalo l'indirizzo della Sitemap
$txt:=$txt+"Sitemap: http://www.sviluppo4d.it/sitemap.xml"+$eol
SEND HTML TEXT($txt)


Codice Codice Calcolo del numero della settimana (2)
Un po' di tempo fa avevo letto un discussione sul calcolo del numero della settimana; mi è capitato di leggere la definizione dell'inizio anno in base alla quale l'anno inizia dal Lunedì della settimana che contiene il 4 gennaio ed ecco qui l'algoritmo corretto



xData:=Date(Request("Data";String(Current date)))

    ` ----------------------------------------------------
    ` User name (OS): llarosa
    ` Date and time: 12/01/09, 13:09:35
    ` ----------------------------------------------------
    ` Method: Calcolo numero settimana
    ` Description Ritorna la settimana dell'anno usando ISO 8601 standard
    ` ( l'anno parte dal Lunedi della settimana che contiene il 4 Gennaio)
    `
    ` Parameters
    ` ----------------------------------------------------

  ` calcolo il 4 di gennaio che giorno è -2 perchè la day number conta come primo giorno la domenica
Jan04DOW:=Day number(Date("04-01-"+String(Year of(xData))))-2
` avendo fatto -2 la domenica diventa -1 e la riporto a 6
Jan04DOW:=(Num(Jan04DOW=-1)*6)+(Num(Jan04DOW#-1)*Jan04DOW)
` calcolo la data del primo lunedi dell'anno
FirstMonday:=Date("04-01-"+String(Year of(xData)))-Jan04DOW
` se la data è inferiore al primo lunedi dell'anno allora appartengono all'ultima settimana dell'anno
` prima quindi calcolo il primo lunedi dell'anno precedente
If (xData<FirstMonday)
` come già descritto ma riferito all'anno precedente
  Jan04DOW:=Day number(Date("04-01-"+String(Year of(xData)-1)))-2
  Jan04DOW:=(Num(Jan04DOW=-1)*6)+(Num(Jan04DOW#-1)*Jan04DOW)
  FirstMonday:=Date("04-01-"+String(Year of(xData)-1))-Jan04DOW
End if
xWeek:=(Int((xData-FirstMonday)/7))+1 ` calcolo della settimana
End if

ALERT(String(xWeek))
3
Codice Codice Metodo EliminaSpazi *
  `Nexus srl 12-5-04
    `toglie gli spazi all'inizio e alla fine
C_STRING(255;$str_s)
C_STRING(1;$spc_s)
C_LONGINT($first_l;$last_l)
$str_s:=$1
$spc_s:=Char(32)
If ($str_s#"")
  $first_l:=1
  $last_l:=Length($str_s)
  While (($first_l<$last_l) & (Substring($str_s;$first_l;1)=$spc_s))
    $first_l:=$first_l+1
  End while
  While (($last_l>=$first_l) & (Substring($str_s;$last_l;1)=$spc_s))
    $last_l:=$last_l-1
  End while
  $str_s:=Substring($str_s;$first_l;$last_l-$first_l+1)
End if
$0:=$str_s

Nota: ho dovuto sostituire l'indicizzazione del carattere $str_s[[$last_l]] con il più solido Substring, perché anche se è corretto formalmente nel caso in cui arrivasse a zero l'ultimo carattere (ad esempio, $1 = " ") la condizione del while dà un errore sulla versione v11.
Info Info [v11 SQL] Passaggio da subtable a tabelle
Nella nota precedente sull'argomento e' stata intrapresa la strada di creare una nuova tabella e trasportarvi i dati della vecchia subtable assegnandogli la relazione nuova.

Secondo me molto difficile da realizzare in quanto bisgna crearsi una pocedura che converta tutte le vecchie subtable in tablelle e poi eventualmente cancelli quelle vecchie inutilizzate.

Io credo di aver trovato un sistema più veloce.

Ho notato che il centenuto del campo in relazione non è altro che il numero del record della tabella in relazione + 1

Eliminate la relazione di tipo subtable (non più ricreabile), i campi ora assumono valore di longint.
Ora con una query potrete ottenere i record correlati tipo

$recnum:=Record number([Tabella_principale])+1
query([Tabella_relazione];[Tabella_relazione]id_added_by_converter=recnum)

Oppure

Ricreare la relazione tra i due campi e nel campo dell'archivio principale inserire il valore del numero del record +1

1
Info Info Alternativa al comando SEND HTTP REDIRECT
Il comando Send Http Redirect in pratica dice al browser che la pagina cercata è stata "temporaneamente" spostato su un altro indirizzo: il browser in modo trasparente all'utente e anche abbastanza velocemente va a cercare il nuovo indirizzo.

Il codice seguente simula la chiamata Send Http Redirect con codice 4d:

`Metodo web_sendHttpRedirect

C_TEXT($1; nuovo_url)
$nuovo_url:=$1

ARRAY TEXT($atHeaders;3)
ARRAY TEXT($atValues;3)
$atHeaders{1}:="X-VERSION"
$atValues{1}:="HTTP/1.0"
$atHeaders{2}:="X-STATUS"
$atValues{2}:="302 Moved Temporarily"
$atHeaders{3}:="Location"
$atValues{3}:=$nuovo_url
SET HTTP HEADER($atHeaders;$atValues)

SEND HTML TEXT($nuovo_url)

1
Stile Stile Uso dell'undescore nei nomi degli oggetti 4D
Capita ovviamente (ed è difficile supporre il contrario) che i nomi dei campi diventino più leggibili inserendo uno spazio che separi due parole.

Diventa in tal caso preferibile utilizzare come separazione un "underscore" [ _ ]; questo approccio è consigliabile perché in tal modo, facendo doppio clic sul campo nel method editor, esso risulterà direttamente e interamente selezionato.
Quindi, invece di

[Anagrafica]Data di nascita

sarà preferibile

[Anagrafica]Data_di_nascita

Inoltre ciò garantirà una migliore compatibilità con chiamate Sql, dove lo spazio non è ammesso fra i nomi dei campi.
Tecniche Tecniche [v11 SQL] Convertire più volte la stessa struttura
Si è visto in faq precedenti le nuove caratteristiche legate a UUID e al file "Catalog.xml". Analizziamo adesso un problema concreto che tali nuove caratteristiche possono portare.

- Convertiamo una struttura e la sua base dati a v11 SQL. Ciò crea una UUID per entrambi.
- Nella struttura convertita vengono inseriti nuovi dati.
- Per motivi di supporto si continua a sviluppare sulla versione 2004 della struttura.
- Scegliamo di convertire alla v11 SQL l'ultima versione della struttura 2004. A questa struttura verrà assegnato un nuovo UUID e ciò impedirà l'apertura del file dati precedentemente convertito e modificato.

La soluzione per eseguire una nuova conversione è quella di prendere il file "Catalog.xml" creato dalla prima conversione a v11 SQL e posizionarlo allo stesso livello della struttura 2004 PRIMA della conversione. Eseguendo a questo punto la conversione, 4D v11 SQL utilizzerà questo "Catalog.xml" per generare lo UUID per la struttura. Così sarà possibile aprire il file dati precedente.
Info Info [v11 SQL] A cosa serve UUID
4D v11 SQL ha introdotto la nuova caratteristica dello UUID per il database.

Eccone alcune caratteristiche.
- Una struttura può aprire più data file, purché tutti abbiano lo stesso UUID della struttura.
- Struttura e file dati possono essere convertiti separatamente (in questo ordine). Possiamo cioè convertire noi la struttura alla v11 SQL e inviarla al cliente; questi aprirà la struttura nuova con il file dati che aveva precedentemente (in versione 2004, 2003, ecc.) e anche questo verrà immediatamente alla v11 SQL senza problemi di UUID.

- Un file dati non può essere aperto da strutture diverse, dove per diverse è da intendere "che abbiano UUID diversi", non "differenti copie della stessa struttura": infatti queste hanno sempre lo stesso UUID.

Novita' Novita' [v11 SQL] Il file Catalog.xml
Convertendo una struttura a 4D v11 SQL Release 3 da una qualsiasi versione precedente, viene creato un file chiamato "Catalog.xml".

Questo file si troverà allo stesso livello della struttura e conterrà tutte le informazioni relative agli oggetti presenti nel database, comprese tabelle, campi e relazioni. Contiene inoltre tutte le informazioni relative allo UUID di ogni oggetto.

Questo file risulta assolutamente necessario per eseguire conversioni "multiple" della stessa struttura: permette infatti di utilizzare dati precedentemente convertiti alla v11 SQL con strutture convertite alla v11 SQL in momenti successivi.
Novita' Novita' [v11 SQL] WEDD e UUID
4D v11 SQL ha introdotto una nuova caratteristica: l'indissolubile collegamento tra struttura e file dati, chiamato UUID (Universally Unique Identifier). Questa caratteristica è decisamente più forte della risorsa WEDD presente nelle versioni precedenti (infatti adesso è stata rimossa). Mentre infatti in precedenza la risorsa WEDD poteva essere aggiunta, la UUID è "obbligatoriamente" inserita all'atto della conversione da una versione precedente alla versione v11 SQL.
Novita' Novita' [v11 SQL] Uso di CAST nelle query SQL di 4D
Una prerogativa dell'SQL Engine è la restituzione di errore in caso di uso di tipi differenti per il confronto. Modifichiamo un precedente esempio come se l'anno di ricerca "1960" fosse stato ad esempio precedentemente chiesto all'utente con una REQUEST o fosse stato salvato in una variabile di tipo testo.

Partiamo al solito dalla query scritta con il linguaggio standard di 4th Dimension:




Ecco la stessa query con BEGIN SQL




Con i comandi SQL




Con QUERY BY SQL




Con EXECUTE IMMEDIATE



Novita' Novita' [v11 SQL] Popolare gli array con i risultati di una SELECT
Vogliamo ottenere un insieme dei film catalogati risalenti almeno all'anno 1960 e con questi risultati popolare deli array. Ecco la query 4D:



Si noti in particolare l'uso di $MovieYear all'interno della query.
A questo punto possiamo mostrare gli array aMovieYear, aTitles, aDirectories, aMedias and aSoldTickets o come scrollable area raggruppate:



o come list box



Ecco la stessa query usando i comandi SQL




Usando QUERY BY SQL:




Usando il comando EXECUTE IMMEDIATE di SQL:




Novita' Novita' [v11 SQL] La clausola WHERE nelle query 4D. Alcuni esempi di SELECT
Volendo sapere quanti film si trovano nella nostra videoteca che siano stati pubblicati dal 1960 in poi scriveremo:




Usando codice SQL, la SELECT diventa:




Usando i comandi SQL del linguaggio 4D, avremmo:




Usando QUERY BY SQL avremmo:




Infine, usando il comando EXECUTE IMMEDIATE all'interno di un blocco SQL avremmo




Si noti nella query SQL l'uso di COUNT(*) per il conteggio dei record.
Info Info Inviare mail HTML con immagini
Per allegare le immagini nelle mail bisogna guardare il protocollo di creazione delle mail ( cfr http://www.faqs.org/rfcs/rfc2557.html ).

Non è che sia semplicissimo, ma spiegazioni ed esempi si trovano molto facilmente in internet. Il nocciolo della questione è che le immagini devono essere trasformate in testo e aggiunte alla mail principale. Bisogna ricordare di aggiungere anche una versione in solo testo per chi usa un lettore di mail che non visualizzi l'html.

Il problema principale è che le mail in html vengono lette dai lettori di posta anche se non sono formattate bene seguendo gli standard; quindi occorre provarne più tipi (gmail, thunderbird, mail pre e post 10.4, outlook express, entourage.. )

C'è un'alternativa, mandi la mail in html e tieni le immagini su un sito.

Il vantaggio in questo caso è che la mail è piccola, lo svantaggio è che le immagini sul sito ci devono stare finché si pensa che l'utente possa riaprire la stessa mail.. quindi dipende molto dal tipo di mail.
Info Info Le parentesi graffe sotto Win senza codice ASCII *
Programmando sotto Windows, per aprire e chiudere le parentesi graffe, solitamente premo il tasto ALT a sinistra della barra spaziatrice e scrivo sul tastierino numerico in sequenza le cifre 123125.

Risulta comunque scomodo sui portatili, dove l'accesso ai NumPad è solitamente condizionato o dall'utilizzo di una "Function key" o dall'attivazione dell'emulazione NumPad sulla tastiera tradizionale.

Recentemente, usando un compilatore Java (linguaggio di programmazione che fa largo uso di graffe), ho scoperto che esiste un altro modo per ottenere lo stesso risultato:
premere il tasto AltGr a destra della barra spaziatrice, il maiuscolo e i due tasti indicanti le parentesi quadre: anche così si ottiene {}

Ecco un elenco completo.

graffa apertagraffa chiusasistema
ALT_GR+7ALT_GR+0GNU Linux
ALT_GR+8ALT_GR+9GNU Linux
ALT+123ALT+125Microsoft Windows
ALT_GR+SHIFT+èALT_GR+SHIFT++Microsoft Windows - GNU Linux
ALT+CTRL+SHIFT+èALT+CTRL+SHIFT++Microsoft Windows
⌥⇧è⌥⇧+Mac OS (con il layout Italiano - Pro)

7
Codice Codice Caricare gli indici all'avvio
In certe situazioni può accadere che l'utente chiuda spesso il programma, ad esempio nelle installazioni monoutenza.

Alla prima ricerca 4D deve caricare gli indici in cache e quindi la prima ricerca puà risultare lenta in modo anomalo all'utente.

Un trucco è caricare gli indici all'avvio del programma in un processo separato: si può lanciare ad esempio una query sui campi più usati.

Se c'è abbastanza cache e il database non è molto grande eseguendo il presente metodo si caricano tutti gli indici all'avvio.

`Metodo CaricaIndici

READ ONLY(*)
MESSAGES OFF
C_LONGINT($fieldType;$fieldLen)
C_BOOLEAN($indexed)
C_REAL($valoreNum)
C_DATE($valoreData)
C_TIME($valoreOra)
C_TEXT($valoreSt)
C_BOOLEAN($valoreBool)
For ($i;1;Count tables)
  For ($j;1;Count fields($i))
    GET FIELD PROPERTIES($i;$j;$fieldType;$fieldLen;$indexed)
    If ($indexed)
      $tabellaptr:=Table($i)
      $campoptr:=Field($i;$j)
      ALL RECORDS($tabellaptr->)
      FIRST RECORD($tabellaptr->)
      Case of
        : (($fieldType=Is Alpha Field ) | ($fieldType=Is Text ))
          $valoreSt:=$campoptr->
          QUERY($tabellaptr->;$campoptr->=$valoreSt)
        : (($fieldType=Is Real ) | ($fieldType=Is LongInt ) | ($fieldType=Is Integer ))
          $valoreNum:=$campoptr->
          QUERY($tabellaptr->;$campoptr->=$valoreNum)
        : ($fieldType=Is Date )
          $valoreData:=$campoptr->
          QUERY($tabellaptr->;$campoptr->=$valoreData)
        : ($fieldType=Is Time )
          $valoreOra:=$campoptr->
          QUERY($tabellaptr->;$campoptr->=$valoreOra)
        : ($fieldType=Is Boolean )
          $valoreBool:=$campoptr->
          QUERY($tabellaptr->;$campoptr->=$valoreBool)
        End case
        UNLOAD RECORD($tabellaptr->)
    End if
  End for
End for
MESSAGES ON
Info Info Correggere manualmente il Build 4D Code del Quick Report
Quando importiamo dentro il nostro codice il codice generato in automatico dal "Build 4D Code" del Wizard del Quick report, sono due le modifiche solitamente da apportare

- Creare una offscreeen area per $ID con
$ID:=QR New offscreen area
e aggiungere dopo il QR Run un
QR DELETE OFFSCREEN AREA($ID)

- Se si sono inserite delle concatenazioni delle stringhe (tipo Codice_fornitore+"_"+Ragione)
il codice risultante sarà:

QR INSERT COLUMN($ID;1;"[Fornitori]Fornitore+\\\"_\\\"+[Fornitori]Ragione")
QR SET INFO COLUMN($ID;1;"Fornitori";"[Fornitori]Fornitore+\\\"_\\\"+[Fornitori]Ragione";0;-152;0;Char(0))

bisogna rimuovere la tripla "\" con una sola, così:

QR INSERT COLUMN($ID;1;"[Fornitori]Fornitore+\"_\"+[Fornitori]Ragione")
QR SET INFO COLUMN($ID;1;"Fornitori";"[Fornitori]Fornitore+\"_\"+[Fornitori]Ragione";0;-152;0;Char(0))

Info Info LAUNCH EXTERNAL PROCESS come Terminale MacOSX o Prompt dei comandi Windows
Abbiamo visto in varie faq l'uso di LAUNCH EXTERNAL PROCESS.

Ribadiamo che è utilizzabile non solo per l'esecuzione di applicazioni o file batch, ma per qualsiasi comando (anche con parametri) del Terminale Apple MacOSX o del Prompt dei comandi Windows.
Non può però eseguire comandi della shell (tipo il comando echo)

Possiamo quindi scrivere direttamente:

LAUNCH EXTERNAL PROCESS ("chmod +x /cartella/miofile.ese")

per cambiare i permessi ad un file MacOS X.

Il comando è di default Sincrono, cioè 4d aspetta che sia eseguito.
Se si volesseeseguirlo in modalità asincrona è necessario lanciare prima il comando
SET ENVIRONMENT VARIABLE con l'opzione _4D_OPTION_BLOCKING_EXTERNAL_PROCESS.
Info Info Una nota sui registratori di cassa
Ultimamente ho dovuto analizzare alcuni protocolli per la comunicazioni con i registratori fiscali.

Ho provato il protocollo XON-XOFF, uno standard per molte casse. La comunicazione si svolge utilizzando degli stream di testo con dei separatori che indicano il tipo di dato. Un protocollo di questo tipo, proprio perché standard, non gestisce però le caratteristiche proprietarie del singolo registratore.

Dovendo in particolare usare i registratori di cassa Kube F della Custom Engineering, ho potuto verificare come utilizzare il protocollo di comunicazione proprietario della cassa fiscale risulta più difficoltoso per la quantità di parametri da gestire, ma consente d'altra parte una gestione completa e accurata della transazione.
Info Info [v11 SQL] Quando il BLOB non passa il testo alla variabile
Sto ancora guardando se sia un bug o un problema del mio codice, intanto, per sicurezza, pubblico il workaround.

Mi capita, lavorando con file di testo Unicode, che il comando BLOB TO TEXT eseguito dopo il DOCUMENT TO BLOB mi restituisca un testo vuoto.

Per evitare questo problema, ho deciso di agire differentemente: in una offscreen area di 4DWrite apro il file; da qui trasferisco il testo nella variabile 4D che veniva lasciata vuota dal BLOB TO TEXT.
3
Info Info Pulire la porta seriale
Capita in alcune comunicazioni seriali che il comando SEND PACKET non venga interpretato correttamente o restituisca risposte strane.
Una buona idea può essere quella di "pulire" la comunicazione seriale prima del SEND PACKET con un RECEIVE BUFFER. Il codice diventa:

RECEIVE BUFFER($buffer)  ` pulisci la seriale
SEND PACKET($Comando)

Plugin Plugin Corrispondenza fra i tipi di dati per DLL Wizard e i tipi 4D
Ecco un elenco sommario delle corrispondenze fra i tipi di dati usati da DLL Wizard e i corrispondenti tipi del linguaggio di 4D.


8-bit: (char, BOOL, BYTE, CHAR, UCHAR, BOOLEAN, CCHAR)

E' possibile passare una variabile integer, longint o real ma le più appropriate sono integer e longint. Passando un real viene effettuata la conversione (a scapito della velocità).


16-bit: (short, WORD, UWORD, SHORT, USHORT)

E' possibile passare una variabile integer, longint o real ma la più appropriata è integer. Passando un real viene effettuata la conversione (a scapito della velocità).


32-bit: (long, word, int, void*, DWORD, LONG...)

E' possibile passare una variabile integer, longint o real, ma la più appropriata è longint. Passando un real viene effettuata la conversione (a a scapito della velocità).


string pointers: (char*, LPCSTR, LPSTR, LPCTSTR, LPTSTR...)

E' possibile passare una variabile alpha o text. Se si usa alpha attenzione alla lunghezza (se si dichiara una variabile 4D di lunghezza inferiore rispetto al dato ottenuto dalla DLL, la stringa viene troncata). Per stringhe olte gli 80 caratteri è necessario passare un text (col limite dei 32.000 caratteri). I caratteri vengono convertiti in modalità ANSI prima di essere inviati alla DLL e, alla ricezione, riconvertiti in ASCII Macintosh.


float values (32 bits): (float)

E' possibile passare una variabile integer, longint o real, ma la più appropriata è real. L'uso di altri tipi può comportare sia rallentamenti (per la conversione) che perdita di dati (i real hanno precisione doppia).


double values (64 bits): (double, GLdouble)

E' possibile passare una variabile integer, longint o real, ma la più appropriata è real. L'uso di altri tipi può comportare rallentamenti.


Plugin Plugin I tipi di dati per DLL Wizard
Per chi, come lo scrivente, usa raramente DLL Wizard, ecco un elenco completo dei tipi di dati gestiti (solitamente, usando DLL Wizard, solo i primi due-tre tipi sono visualizzati):
  • 8-bit:
    char, BOOL, BYTE, CHAR, UCHAR, BOOLEAN, CCHAR
  • 16-bit:
    short, WORD, UWORD, SHORT, USHORT
  • 32-bit:
    long, word, int, short*, word*, long*, int*, void*, DWORD, LONG, LPVOID, UINT, GLOBALHANDLE, HANDLE, HLOCAL, LPDWORD, LPBOOL, LPBYTE, LPWORD, LPLONG
  • string pointers:
    char*, LPCSTR, LPSTR, LPCTSTR, LPTSTR, NPSTR, PCSTR, PCWSTR, PSTR, PTSTR
  • double values (64 bits):
    double, GLdouble
  • float values (32 bits):
    float
  • void return:
    void, VOID
Comandi Comandi Aggiungere un elemento agli array di una listbox: INSERT LISTBOX ROW
Il caso è banale: aggiungere a tutti gli array di una listbox un elemento (possibilmente in fondo) per inserire una nuova riga.

Una possibilità è aggiungere un elemento ad ognuno degli array che compongono la listbox.
Oppure si può usare

INSERT LISTBOX ROW ({*; }object; position)

per inserire un elemento in posizione position a tutti gli array della listbox.


In ogni caso poi bisogna usare EDIT ITEM per modificare l'elemento inserito.
Comandi Comandi Differenza fra EDIT ITEM e GOTO AREA
I comandi EDIT ITEM e GOTO AREA servono per spostarsi su una certa zona di un form. La differenza sta nel tipo di oggetti su cui tali comandi si applicano.

EDIT ITEM ({*; }object{; item})

permette di modificare gli elementi di:

  • Liste gerarchiche
  • List box
  • Subforms (in questo caso, solo il nome della subform può essere passato come object),
  • List forms mostrate con MODIFY SELECTION o DISPLAY SELECTION.

item indica l'elemento da modificare, se utilizzabile.

In tutti gli altri casi si usa GOTO AREA.
Novita' Novita' [v11 SQL] Uso di INSERT
Continuiamo nella presentazione dei comandi SQL parlando di INSERT. La sintassi è assai agevole:

INSERT INTO {sql_name | sql_string}

[(column_reference, ..., column_reference)]


{VALUES({arithmetic_expression |NULL}, ..., {arithmetic_expression |NULL}) |subquery}




dove {sql_name | sql_string} denotano in qualche modo la tabella dove eseguire gli inserimenti, >[(column_reference, ..., column_reference)] sono i campi dove verranno inseriti i dati, e i dati possono essere espressioni o anche altre sottoespressioni SQL. Vediamo di esempi:


INSERT INTO table1 (SELECT * FROM table2)

inserisce in table2 tutti i record di table1



CREATE TABLE ACTOR_FANS
           (ID INT32, Name VARCHAR);
      INSERT INTO ACTOR_FANS
           (ID, Name)
           VALUES (1, 'Francis');


crea la tabella ACTOR_FANS, definisce i campi e di seguito inserisce i valori nella tabella.
Info Info Misurare le prestazioni di un metodo
Un rapido esempio per misurare le prestazioni di un metodo contando il numero di millisecondi:

$vrMillisecondsStart:=Milliseconds

`qui fai qualsiasi cosa

ALERT("Tempo trascorso: "+String(Milliseconds-$vrMillisecondsStart))

Oltre a Milliseconds, si possono usare equivalentemente anche Current time o Tickcount.
Info Info 4D, demo web 2.0 e iPhone / iPod Touch
Il pacchetto 4d Web 2.0 Pack contiene varie cose, fra le quali anche la libreria Ajax Framework ..

Per vedere una demo ben spiegata è possibile guardare gli esempi on line su questo sito, dove ci sono le tecniche da utilizzare:

http://demo.4d.com:8081/

Una demo di un'applicazione web 2.0 completa (con tecniche particolari come il drag & drop) si trova a questo indirizzo:

http://demo.4d.com:8081/demos/wow/personal_planner.html

Infine sono disponibili due modi per costruire applicazioni per iPhone/iPod touch.

  1. Opzione Zero Codice: guarda questo esempio con iPhone/iPod touch su questo link:
    http://demo.4d.com:8081/ipe.html

  2. Opzione personalizzabile: guarda questo esempio con iPhone/iPod touch su questo link:
    http://demo.4d.com:8081/ipedemo/index.html


In queste ultime demo si vede anche l'opzione Offline, perché sono incluse le tecniche per far funzionare tutto anche quando non si è collegati, usando Html 5 o le librerie Google Gears,

Safari 3 e Firefox 3 supportano HTML 5. Gli utenti di Internet Explorer 7 e Firefox 2+ devono installare Google Gears per andare offline.
Info Info [v11 SQL] Coordinate di un clic sulle immagini
4D dà le coordinate di un click sulle immagini nelle due variabili di sistema MouseX e MouseY: contengono il numero di pixel di distanza dal punto in alto a sinistra dell'immagine (0,0).

La cosa veramente interessante è che se l'immagine è mostrata con il formato "Truncated non-centered" le coordinate sono corrette automaticamente in funzione anche di eventuale zoom o spostamento dell'immagine nel riquadro che la ospita.

Le due variabili vanno valutate all'interno di un form event On Clicked oppure On Double Clicked.
Tecniche Tecniche Indirizzare la stampa su un cassetto della stampante
In linea di massima (solo sotto Windows):

  1. usi il comando PRINT OPTION VALUES ( Paper source option; array_Nomi; array_ID)
  2. il primo array contiene i nomi dei cassetti, il secondo il corrispondente ID
  3. prima di stampare scrivi SET PRINT OPTION ( Paper source option; array_ID{ x } ) dove x è il numero del cassetto scelto dalla lista array_Nomi
Bug Bug 4D v_11 caratteri ascii dal 1 al 8
4D V 11.2 In preferenze non settato unicode mode

$Test:=Char(5) ` dal 1 al 8

Non funziona

If ($Test#"")
` fai qualche cosa
End if


Sostituito con:

If (Position(Char(5);$Test;1)>0)
` fai qualche cosa
End if

Info Info Ajax o Flex?
Scritto in parole povere e per programmatori che vogliono sapere il succo della cosa:

Web 2.0 è in linea di massima il sistema di far funzionare una pagina del browser come se fosse un'applicazione senza doverla ricaricare, com'è normale nella navigazione web. Quindi l'utente rimane nella stessa finestra e la sensazione è più simile a quella di un applicativo in locale.

Ajax utilizza pagine html, css e javascript : le singole chiamate al sito sono processate all'interno del codice javascript con un comando che fa la richiesta al server e aspetta la risposta senza appunto cambiare pagina.

Flex è un sistema di Adobe per fare la stessa cosa in modo più veloce perché usa il Flash come se fosse un runtime; però bisogna usare actionscript e pagine mxml e compilare ogni volta che si fa una modifica producendo dei file swf. Il vantaggio oltre alla velocità di esecuzione è che il risultato è molto più browser indipendente.

Entrambe le soluzioni in teoria non costano niente, perché usano librerie software opensource. Anche l'SDK di flex è opensource e scaricabile gratuitamente, Adobe vende il sistema di editing (basato sul progetto opensource Eclipse) che rende più agevole il ciclo di produzione.

4d per entrambe le soluzioni ha preparato delle librerie o framework (che credo siano distribuite dentro 4d Web 2.0 Pack) che permettono di utilizzare nel modo più diretto possibile sia con la versione 2004 che con la v11.
Novizi Novizi Scorciatoie da tastiera *
Ecco alcune scorciatoie utilizzabili da tastiera:

- Chiusura di tutte le finestre di design (eccetto la struttura): Alt+Clik su uno qualsiasi dei bottoni di chiusura.

- Chiusura di tutte le finestre di design passando alla modalità User o Custom: Shift mantre si cambia modalità.

- Aprire il metodo selezionato: Ctrl+p.

- Rinominare gli oggetti solo selezionabili: Ctrl+Clic sul nome.

- Copiare il testo selezionato in un posto specifico degli appunti: Ctrl+Shift+un numero da 1 a 9.

- Incollare il testo da un posto specifico degli appunti:Ctrl+un numero da 1 a 9.

- Selezionare tutti gli oggetti di uno stesso tipo in un form: Ctrl(Command su Mac)+clik su uno degli oggetti da selezionare.

- Selezionare/deselezionare tutti gli eventi relativi ad un oggetto: Ctrl(Command su Mac)+clik su un evento.

- Creare un subrecord: Ctrl(Command su Mac)+/ (questa impostazione è modificabile usando 4D Customizer Plus).


4
Novita' Novita' [v11 - Conferenza] SQL injection
Una delle maggiori novità della versione 11 di 4D è l'uso dell'SQL. E' però necessario fare attenzione ad gli inserimenti all'interno dell'ambiente web per creare stringhe SQL. Facciamo il seguente esempio.

Supponiamo di avere:

SELECT * FROM Contact WHERE Name = :vName

dove vName è una variabile proveniente dalla pagina web. Supponiamo che lo smaliziato utente web abbia scritto nella variabile vName

Pippo; DELETE * FROM Contact

Ci ritroveremmo la tabella totalmente svuotata.
1
Novita' Novita' [v11 - Conferenza] Uso dei nuovi tipi di indice [1]
La v11 mette a disposizione una svariata serie di indici. Eccone una breve spiegazione con relativo utilizzo:

- BTree index è da usare per valori non ripetuti (ID dei record, ragioni sociali, nomi, ecc.)
- Cluster index per valori ripetuti (booleani, categorie, sesso, ecc.)
- Composite index (indici compositi) per gli ordinamenti(Nome + Cognome, Città + CAP, ecc.)
- Keyword index per i testi.
Info Info [v11 - Conferenza] Uso dei nuovi tipi di indice [2]
Alcune info aggiuntive sugli indici della v11.
Il sistema di realizzazione del keyword index non è personalizzabile al momento, ma lo sarà.

Se si usa QUERY su un campo indicizzato sia normalmente che con keyword index, quest'ultimo indice ha la precedenza (la ricerca verrà fatta sulle parole).

Con il keyword index non si può usare la sintassi "@TESTO@", ma si può usare quella "TESTO@".

Al momento, se si sceglie come tipo di indice "Automatic", 4D userà il cluster per i booleani, il BTree per gli altri campi, ma nelle versioni future l'algoritmo di scelta dell'indice verrà raffinato.
Info Info Se 4D si apre dopo un minuto di inspiegabile attesa...
Mi capitava spesso di assistere ad un comportamento strano di 4D. Facendo doppio clic sulla struttura da aprire, il programma restava per circa un minuto in uno stato di attesa senza alcun apparente motivo. Passata questa attesa il programma si apriva regolarmente. Allo stesso modo, l'apertura della finestra di impostazioni di stampa aveva gli stessi tempi d'attesa.

Il motivo è da ricercare nei driver di stampa delle stampanti in rete. Se la stampante predefinita del computer in questione è una stampante condivisa proveniente da un'altra macchina o da un print server, con alcuni driver si hanno i tempi di attesa descritti.

Una soluzione può essere quella di utilizzare come stampante predefinita una stampante "falsa", e poi scegliere la stampante corretta in funzione della stampa richiesta.
Info Info ONE RECORD SELECT
Il comando ONE RECORD SELECT usa la sintassi:

ONE RECORD SELECT {(table)}

e permette di ridurre la selezione della tabella table (se omessa è la default table) al record corrente. Se non esiste un record caricato in memoria o non esiste un record corrente il comando non ha effetto.
Tecniche Tecniche Come mettere gli Help Tips da programma
Cito una bella soluzione di Pat Bensky, dal Nug americano:

Noi abbiamo definito un unico HelpTip. Questo contiene solamente

<HelpText>

HelpTip è selezionato per ogni oggetto su cui vogliamo che appaia l'aiuto.
Dentro l'oggetto il codice è così:

Case of
  :(form event=on mouse enter)
    HelpText:="quello che vuoi scrivere in questo aiuto"
End case
Novita' Novita' [v11 SQL] Quattro modi per utilizzare SQL [4]
Partiamo sempre dalla query 4D



Un quarto modo per interagire col motore SQL di 4D è quello di utilizzare il comando EXECUTE IMMEDIATE all'interno di un blocco Begin SQL - End SQL. La query diventa:

Novita' Novita' [v11 SQL] Quattro modi per utilizzare SQL [3]
Partiamo sempre dalla query 4D



Un terzo modo per ottenere gli stessi dati via SQL è quello di utilizzare il comando QUERY BY SQL. In questo caso l'esempio diventa:



Infatti il comando QUERY BY SQL non fa altro che eseguire una semplice SELECT del tipo:

SELECT *
FROM myTable
WHERE <SQL_Formula>

dove myTable è la tabella passata come primo parametro a QUERY BY SQL, e SQL_Formula il secondo parametro; otterremmo:

QUERY BY SQL(myTable;SQL_Formula)

Nel nostro caso, in cui non esiste una clausola WHERE, ne forziamo l'esistenza con "ID <> 0" e la query SQL eseguita diventa:

SELECT *
FROM MOVIES
WHERE ID <> 0
Novita' Novita' [v11 SQL] Quattro modi per utilizzare SQL [2]
Partiamo sempre dalla solita query 4D



Un secondo sistema per ottenere dati da 4D usando SQL è quello di eseguire una query ODBC usando come origine dei dati lo stesso database 4D. L'esempio diventa:



dove al comando ODBC LOGIN viene associata come fonte dati SQL INTERNAL.
Novita' Novita' [v11 SQL] Quattro modi per utilizzare SQL [1]
Supponiamo di avere la seguente query in modalità 4D



Un primo modo per ottenere lo stesso risultato via SQL diventa il seguente:




dove il riferimento a $AllMovies viene effettuato attraverso i caratteri speciali "<<" e ">>"

Una sintassi equivalente è data dall'uso di ":"




Una attenzione particolare deve essere posta alle variabili interprocesso, dove diventa necessario l'uso delle parentesi quadre:


Plugin Plugin 4D Chart: CT Chart arrays
CT Chart arrays crea un grafico a partire dai dati memorizzati in alcuni array.
La sintassi è:

CT Chart arrays (area; type; size; categoryArray; seriesArray; valuesArray)

dove "area" è l'area 4D Chart, "type" è il tipo di grafico, "size" è la dimensione iniziale del grafico, "categoryArray", "seriesArray", e "valuesArray" sono gli array contenenti x, y e z del nostro grafico.

I tipi di grafico passabili in "type" sono:

1 per il grafico di tipo Area
2 per il grafico di tipo Column
3 per il grafico di tipo Picture
4 per il grafico di tipo Line
5 per il grafico di tipo Scatter
6 per il grafico di tipo Pie
7 per il grafico di tipo Polar
8 per il grafico di tipo 2D XY
100 per il grafico di tipo 3D Column
101 per il grafico di tipo 3D Line
102 per il grafico di tipo 3D Area
103 per il grafico di tipo 3D Surface
104 per il grafico di tipo 3D Triangle
105 per il grafico di tipo 3D Spike

A "size" possono essere assegnati i valori

1 = Variabile
2 = Relativa alla finestra (Auto-Variable)
3 = Relativa al grafico (Auto-Document)

L'array "categoryArray" è l'array delle X
L'array "seriesArray" è l'array delle serie. In un grafico bidimensionale è mostrato nell'asse X, nei tridimensionali è la Y
L'array "valuesArray" è l'array contenente i valori.


1
Tecniche Tecniche Controllo della correttezza di una password: il nuovo Position
Abbiamo visto in una faq precedente come confrontare due stringhe, ad esempio la password inserita da un utente.

La nuova versione di 4D, v11 SQL, permette di evitare il ciclo sul controllo del codice Ascii (adesso Character code) dei caratteri. Si può infatti usare in sostituzione il comando Position, che ha assunto questa rinnovata sintassi:

Position (find; aString; start; lengthFound; *)

dove

- find è la stringa da cercare;
- aString è la stringa dove effettuare la ricerca;
- start è il numero che l'iesimo carattere della stringa aString da cui iniziare la ricerca;
- lengthFound se specificato, è una variabile che conterrà la lunghezza della stringa trovata in aString (necessaria quando si cerca æ e si trova ae, ß e si trova ss, ecc., nei due casi citati lengthFound varrebbe 2);
- * se specificato, effettua la ricerca in maniera diacritica, distinguendo cioé maiuscole da minuscole, accentate da normali, ecc.

Nel nostro caso, il confronto fra la stringa e la password sarebbe:

$position:=Position($pwd;ThePassword;1;$length;*)
$uguali:=(($position=1) & ($length=Length(ThePassword)))


Info Info 4D Chart: CT SET LABEL ATTRIBUTES
CT SET LABEL ATTRIBUTES permette di impostare la modalità di visualizzazione delle etichette.
La sintassi è:

CT SET LABEL ATTRIBUTES (area; object; axis; position; orientation; format{; frequency})

dove:
- area è l'area di Chart
- object è il grafico
- axis è l'asse su cui agire:
      0 = Category
      1 = Series
      2 = Values
- position è la posizione dell'etichetta:
      -1 = No modifica
      0 = Nessuna
      1 = Sopra
      2 = Sinistra
      3 = Sotto
      4 = Destra
- orientation è l'orientamento:
      -1 = No change
      0 = Normal
      1 = Vertical
      2 = Rotated right
      3 = Rotated left
      4 = Staggered
      5 = Wrap around
- format è una stringa che imposta il formato di visualizzazione
- frequency è un intero (opzionale) che specifica ogni quanti valori visualizzare l'etichetta; il parametro è molto utile per i grafici molto "densi".

Ecco ad esempio come visualizzare le etichette sull'asse X ogni 3 valori:

CT SET LABEL ATTRIBUTES (Area;$Chart;0;-1;-1;"";3)

Plugin Plugin 4D Chart: CT SET PROPERTIES
CT SET PROPERTIES permette di impostare le proprietà dell'area 4D Chart.

La sintassi è:

CT SET PROPERTIES (area; printOrder; changeAlert; hotlinkType; saveAlert)

dove
- area è l'area di Chart
- printOrder è l'ordine di stampa (non l'orientamento): 0 in orizzontale, 1 in verticale, -1 non modificare
- changeAlert mostra un alert se l'utente prova a modificare l'area
- hotlinkType è un parametro obsoleto, usare come valore -1
- saveAlert per chiedere all'utente il salvataggio dell'area.

Per changeAlert e saveAlert e valori utilizzabili sono: 0 nessun messaggio, 1 avverti, -1 non modificare l'impostazione.

Tecniche Tecniche [v11 SQL] Memorizzazione dei BLOB
Una tecnica che molto presto i programmatori di 4D hanno dovuto imparare è stata quella riguardante lqa gestione dei BLOB. Molto spesso, infatti, gli sviluppatori si vedevano costretti a memorizzare i BLOB in tabelle separate rispette a quelle dei record di appartenenza dei BLOB stessi in modo da velocizzare i tempi di accesso al record, poiché infatti anche in modalità "lista" il record veniva completamente caricato.

Tale workaround con la v11 non è più necessario, poiché infatti i BLOB vengono caricati solo quando viene carica to il dettaglio del record, velocizzando così notevolmente l'accesso alle liste dei dati.
Info Info [v11 SQL] I file .RSR e .4DR
Con la nuova versione di 4D sono scomparsi i file .RSR e .4DR.

Per essere più precisi, in realtà dei file di risorsa personalizzati possono essere creti e mantenuti all'interno della Cartella Resources presente sia nella cartella contenente la struttura che nella cartella dell'applicazione 4th Dimension.
Plugin Plugin 4D Chart: CT MOVE
il comando CT MOVE permette di spostare degli oggetti all'interno dell'area di chart. La sibntassi è:

CT MOVE (area; scope; newLeft; newTop)

dove
area è la'area di Chart;
scope indica a quali oggetti applicare il movimento: -1 indica tutti gli oggetti dell'area, 0 indica gli oggetti attualmente selezionati, un altro numero maggiore di 0 indica l'ID dell'oggetto a cui applicare l'azione;
newLeft e newTop sono le nuove coordinate.

Info Info [v11 SQL] I file di indice
Con la nuova verione di 4D il sistema di indicizzazione è cambiato radicalmente.

Gli indici sono memorizzati in due file separati:
- il file .INDX è il file che contiene gli indici per i dati;
- il file .INDY è il file che contiene gli indici per la struttura.

I file si trovano o nella cartella del database (su Windows) o nel package del database (su Mac).

Questo approccio ha dei vantaggi non indifferenti:
- non viene eseguito un backup degli ndici;
- un indice danneggiato non danneggia i dati;
- per ricreare tutti gli indici basta cancellare il file INDX: alla successiva apertura del database 4D cercherà il file e, non trovandolo, lo creerà nuovamente in autimatico.
Plugin Plugin 4D Chart: CT SET CHART COORDINATES
Il comando di 4D Chart CT SET CHART COORDINATES permette di impostare la posizione di un grafico all'interno dell'area.
La sintassi è:

CT SET CHART COORDINATES (area; object; left; top; right; bottom)

dove
area è l'area di 4D Chart
object è il grafico creato, ad esempio, con CT Chart arrays
left; top; right; bottom sono le coordinate del grafico (in punti).
Plugin Plugin 4D Chart: CT SET LEGEND TEXT
CT SET LEGEND TEXT permette di impostare i titoli delle serie (o delle categorie per i grafici a torta, i pie) di un grafico. La sintassi è:

CT SET LEGEND TEXT (area; object; legendItem; legendtext)

dove
area è l'area di 4D Chart
object è il grafico di cui si vogliono impostare i titoli nella legenda
legendItem rappresenta la n-esima serie di cui vogliamo impostare il titolo
legendtext è il testo che verrà usato come titolo.

Si noti che se anche si usa la legenda in modalità reverse (cioé in ordine inverso rispetto a quello di partenza), avendo applicato il comando CT SET LEGEND ATTRIBUTES, legendItem continua a seguire l'ordine originario.

Plugin Plugin 4D Chart: CT SET DISPLAY
CT SET DISPLAY permette di visualizzare o meno le barre di un'area 4DChart.
La sintassi è:

CT SET DISPLAY (area; item; displayCode)

dove "area" è l'area 4D Chart, "item" è la zona e "displayCode" è la modalità di viasualizzazione

Il valore di "item" può essere:

1 Menu Bar
2 Chart Tools
3 Object Tools
6 Scroll Bars
9 Rulers

Il valore di "displayCode" può essere:

0 = Nascondi
1 = Mostra
2 = Cambia


Web Web Inviare immagini dinamiche in una pagina html
Per caricare una immagine dinamica, cioè inviata da 4d in funzione di alcuni parametri o calcoli, in una pagina web occorre inserire un tag del tipo:

<img src="/4DACTION/webImmagine?parametro=<!--#4dvar vParametroEsempio-->" alt=""/>

Il metodo webImmagine (che deve essere impostato nelle proprietà come disponibile a 4DAction) contiene un codice simile:

... `qui il codice elabora l'immagine usando il parametro passato
$immagine:=CT Area to picture (AreaVirt;-2)
PICTURE TO GIF($immagine; $mioBlob)
SEND HTML BLOB($mioBlob;"image/gif")

Con il formato GIF si mantiene meglio la grafica di un'immagine creata con Chart e contenente testo e grafici lineari.

Alternativamente, se l'immagine è ad esempio un disegno o una foto potrebbe essere meglio utilizzare il formato Jpeg; allora il codice sarebbe il seguente:

... `qui sempre usando il parametro passato il codice recupera un'immagine da disco
$percorso:="Hd:Immagini:"+vParametroEsempio
READ PICTURE FILE($percorso;$Immagine)
PICTURE TO BLOB($Immagine;$mioBlob;"JPEG")
SEND HTML BLOB($mioBlob;"image/jpeg")

(grazie ai contributi di Sandro Bonin e Giuseppe Scarafoni)
Novita' Novita' [v11.1 SQL] DESCRIBE QUERY EXECUTION
Il comando DESCRIBE QUERY EXECUTION è stato aggiunto nella versione 11.1 del sistema di sviluppo per permettere una migliore analisi delle query.

Passando al comando un parametro booleano True si attiva la possibilità di chiedere a 4th Dimension di tenere traccia di come viene eseguita una query (sia essa SQL o del linguaggio 4D). Le query infatti sono ottimizzate da 4D per essere efficenti al massimo, ma non è detto che il nostro modo di pensare una ricerca sia migliore da un punto di vista prestazionale rispetto alla stessa ricerca eseguita in altro modo.

Gli aspetti che 4D permette di analizzare sono:
- come viene pianificata la query (query plan);
- come viene eseguita la query (query path).

Tali informazioni vengono ottenute tramite i nuovi comandi Get Last Query Plan e Get Last Query Path.

Ecco un esempio:

C_TEXT ($vResultPlan;$vResultPath)
ARRAY TEXT (aTitles;0)
ARRAY TEXT (aDirectors;0)
DESCRIBE QUERY EXECUTION (True) `analysis mode

Begin SQL
      SELECT ACTORS.FirstName, CITIES.City_Name
      FROM ACTORS, CITIES
      WHERE ACTORS.Birth_City_ID=CITIES.City_ID
      ORDER BY 1
      INTO :aTitles, :aDirectors;
End SQL

$vResultPlan:=Get Last Query Plan (Description in Text Format)
$vResultPath:=Get Last Query Path (Description in Text Format)
DESCRIBE QUERY EXECUTION (False) `End analysis mode
Codice Codice Numeri da cifre a lettere
Prima di tutto complimenti per il sito, non solo perché è davvero bello ma perché si vede che c'è chi ci lavora, quindi complimenti a chi lo fa.

Mando un frammento di codice che ritengo interessante anche se forse non hamolte occasioni d'impiego; è un method che converte un valore numerico nella corrispondente stringa descrittiva in lettere, esempio 8519 =
"ottomilacinquecentodiciannove".

Non ho trovato nulla di già fatto per 4D (in italiano). La conversione avviene nel formato usuale per valori in euro con decimali, come si usa per gli assegni, per esempio "trecentoventi/23centesimi"; è modificabile e si
puo facilmente omettere o modificare la parte decimale.
E' abbastanza compatto, contrariamente a quanto io stesso avevo supposto accingendomi a scriverlo.

Uso: passare un valore di tipo reale e torna un tipo testo. Accetta valori
da 1 a 999.999.999, genera un messaggio di errore se si passa un valore fuori range.



C_REAL($1;$valore)
C_TEXT($0;$decimale;$stringa)

ARRAY INTEGER($base;8)
ARRAY TEXT($descrizione;3)
C_INTEGER($X;$gruppo;$valoreDecine)

ARRAY TEXT($nome_unità;9)
$nome_unità{0}:=""
$nome_unità{1}:="uno"
$nome_unità{2}:="due"
$nome_unità{3}:="tre"
$nome_unità{4}:="quattro"
$nome_unità{5}:="cinque"
$nome_unità{6}:="sei"
$nome_unità{7}:="sette"
$nome_unità{8}:="otto"
$nome_unità{9}:="nove"

ARRAY TEXT($nome_10_20;9)
$nome_10_20{0}:=""
$nome_10_20{1}:="undici"
$nome_10_20{2}:="dodici"
$nome_10_20{3}:="tredici"
$nome_10_20{4}:="quattordici"
$nome_10_20{5}:="quindici"
$nome_10_20{6}:="sedici"
$nome_10_20{7}:="diciassette"
$nome_10_20{8}:="diciotto"
$nome_10_20{9}:="diciannove"

ARRAY TEXT($nome_decine;10)
$nome_decine{0}:=""
$nome_decine{1}:="dieci"
$nome_decine{2}:="venti"
$nome_decine{3}:="trenta"
$nome_decine{4}:="quaranta"
$nome_decine{5}:="cinquanta"
$nome_decine{6}:="sessanta"
$nome_decine{7}:="settanta"
$nome_decine{8}:="ottanta"
$nome_decine{9}:="novanta"
$nome_decine{10}:="cento"

$decimale:=Replace string(String(Dec($1));"0,";"")
$decimale:=$decimale+("0"*Num(Length($decimale)=1))
If ($decimale="00") `è uno zero non lettera o
  $decimale:="zero"
End if
$valore:=Int($1)

If ($valore=0) | ($valore>=(10^9)) ` valori accettati per la parte intera da 1 a 999.999.999
  $0:="### ERRORE valore fuori range"
Else
  For ($X;8;0;-1) ` calcola le basi per gli esponenti da 0 a 8 - esempio 825 = 8*(10^2) + 2*(10^1) + 5*(10^0)
    $base{$X}:=Int($valore/(10^$X))
    $valore:=$valore-($base{$X}*(10^$X))
  End for
  $gruppo:=3 `elabora separatamente milioni, migliaia e unità, nell'ordine (si potrebbe anche procedere per ordine inverso, non cambia)
  For ($X;8;0;-3)
    If ($base{$X}=1)
      $descrizione{$gruppo}:="cento"
    Else
      $descrizione{$gruppo}:=$nome_unità{$base{$X}}+("cento"*Num($base{$X}>0))
    End if
    $valoreDecine:=$base{$X-1}*10+$base{$X-2}
    If ($valoreDecine=1) & ($base{$X}=0)
      $descrizione{$gruppo}:=$descrizione{$gruppo}+("unmilione"*Num($gruppo=3))+("mille"*Num($gruppo=2))+("uno"*Num($gruppo=1))
    Else
      If ($valoreDecine>10) & ($valoreDecine<20) ` se >10 e <20 usa i nomi unidici, dodici, ecc
        $descrizione{$gruppo}:=$descrizione{$gruppo}+$nome_10_20{($valoreDecine-10)*Num(($base{$X-1})>0)}
      Else `altrimenti usa i nomi delle decine + i nomi delle unità
        If ($base{$X-2}=1) | ($base{$X-2}=8) ` se nome unità inizia per vocale omette l'ultima vocale del nome delle decine (ventuno e non ventiuno)
          $stringa:=Substring($nome_decine{$base{$X-1}};1;Length($nome_decine{$base{$X-1}})-1)
        Else
          $stringa:=$nome_decine{$base{$X-1}}
        End if
        $descrizione{$gruppo}:=$descrizione{$gruppo}+$stringa+$nome_unità{$base{$X-2}}
      End if
      $descrizione{$gruppo}:=$descrizione{$gruppo}+(("milioni"*Num($gruppo=3))*Num($descrizione{$gruppo}#""))
      $descrizione{$gruppo}:=$descrizione{$gruppo}+(("mila"*Num($gruppo=2)*Num($descrizione{$gruppo}#"")))
    End if
    $gruppo:=$gruppo-1 `elabora il gruppo successivo
  End for
  $0:=$descrizione{3}+$descrizione{2}+$descrizione{1}+" e "+$decimale+" centesimi" `concatena i nomi dei 3 gruppi: milioni, migliaia, unità + i decimali
End if

1
Novita' Novita' [v11 SQL] Confronto di caratteri ascii inferiore a 32
Dalla versione v11 SQL il parametro di confronto = non distingue più i caratteri da 1 a 8 e da 14 a 31.

Quindi il Char(2) = Char(3), esattamente come fa con "A"="à".

La cosa è importante per chiunque abbia sviluppato procedure di controllo del flusso delle seriali, ad esempio dove appunto questi caratteri servono come delimitatori.

La soluzione è ovviamente quella di confrontare il Character Code, cioè:

Character code(Char(2)) # Character code(Char(3))
Comandi Comandi [v11 SQL] Il comando OPEN SECURITY CENTER
Dalla versione v11 non è più necessario utilizzare un applicativo 4D Tools per le operazioni di manutenzione.

Le funzionalità sono ora disponibili all'interno dell'applicazione, nella cosiddetta MSC, cioè Maintenance and Security Center.

Per dare la possibilità all'utente finale di accedere alla stessa finestra, è possibile utilizzare il comando OPEN SECURITY CENTER. Alcune funzioni vengono automaticamente disabilitate in funzione dei privilegi dell'utente corrente.
Info Info [v11 SQL] Licenze di distribuzione dei pacchetti monoutente
Dalla versione v11 ci sono alcune novità nella distribuzione degli applicativi prodotti.
Innanzitutto non occorre più acquistare un engine, ma il runtime è incluso gratuitamente nel pacchetto di sviluppo.
Inoltre nel runtime Unlimited Desktop sono inclusi senza spese aggiuntive il 4D Write e il 4D View.

Ecco uno schema delle varie opzioni:

4D Interpreted Desktop
Questa licenza è inclusa nel pacchetto di sviluppo 4D Developer Standard e permette la distribuzione di applicazioni in monoutenza non compilate con il runtime 4D Desktop.

4D Unlimited Desktop
Questa licenza è inclusa nel pacchetto di sviluppo 4D Developer Professional e permette la distribuzione di applicazioni in monoutenza sia interpretati che compilati con 4D Desktop oltre che alla creazione di applicazioni incorporando la struttura nel runtime. Questa licenza include senza costi aggiuntivi le espansioni 4D Write e 4D View.

4D Web Application Server
Questa licenza permette agli sviluppatori di distribuire applicazioni compilate o interpretate con 4D Desktop che abbiano la funzionalità di Server Web e di Server Web Services.
Questa licenza include anche le funzionalità SQL Pass Through e ODBC Login per permettere al database di collegarsi ad altre fonti di dati. La licenza è venduta a postazione e non è inclusa nei pacchetti di sviluppo.

4D SQL Desktop
Anche questa licenza non è inclusa nei pacchetti di sviluppo: acquistandola gli sviluppatori possono distribuire applicazioni compilate o interpretate con 4D Desktop con la possibilità di collegarsi a fonti di dati esterne utilizzando le funzionalità SQL Pass Through e ODBC Login. Questa opzione include anche le espansioni 4D Write e 4D View.
Comandi Comandi [v11 SQL] Il comando CHECK LOG FILE
Il comando CHECK LOG FILE è assai potente: l'accesso ad esso dovrebbe essere ristretto agli utenti di più alto livello.

CHECK LOG FILE permette di consultare il file log e di tornare indietro (Rollback) ad uno stadio precedente della base dati.



Il comando è utilizzabile o via Runtime monoutente o da 4DServer, non da un client.

Novita' Novita' [v11 SQL] Aprire un file trascinato sull'icona di 4d
Dalla versione v11 è disponibile il metodo del database On Drop, sia in monoutenza che dal client.

Il metodo viene eseguito quando si trascina un file dalla scrivania sull'icona di 4d ( o su un'area vuota della finestra contenitore in Windows); normalmente è eseguito solo se l'applicazione è già aperta, però nel caso in cui il programma sia compilato con il Runtime (chiamato ora 4D Desktop) viene lanciato anche all'apertura.

Adesso è possibile ad esempio aprire un documento di 4d Write trascinandolo sull'icona dell'applicativo compilato; ecco un esempio di codice da usare dentro il metodo On Drop:

fileTrascinato_s:=Get file from pasteboard(1)
If (Position(".4W7"; fileTrascinato_s)=Length(fileTrascinato_s)-3)
  areaEsterna:=Open external window(100;100;500;500;0;droppedFile;"_4D Write")
  WR OPEN DOCUMENT(areaEsterna; fileTrascinato_s)
End if

Info Info Errori durante il Backup automatico
Dalla versione 2004 il backup è stato integrato in 4D.

Quando c'è un problema, sulla schermata del Server è possibile vedere il codice dell'errore. Ecco una breve descrizione:

  • 1404 : C'era una transazione aperta in un processo
  • 1405 : Raggiunto il timeout in attesa della fine delle transazioni
  • 1406 : Annullato dall'utente
  • 1407 : Percorso di destinazione non valido
  • 1408 : Errore durante il backup del file di log
  • 1409 : Errore durante il backup di lettura dell'originale o di scrittura della copia
  • 1410 : Non riesco a trovare la copia da verificare
  • 1411 : Errore durante la verifica della copia
  • 1412 : Non riesco a trovare la copia del file di log da verificare
  • 1413 : Errore durante la verifica della copia del file di log


Info Info [v11 SQL] Usare 4D come SQL Server
E' possibile accedere via ODBC a 4D, anche monoutenza, per utilizzarlo come SQL Server. Vi si può accedere da un programma come Crystal Report, Excel o anche un altro applicativo 4D.



La funzionalità può essere avviata e stoppata da menu o da linguaggio con i comandi START SQL SERVER e STOP SQL SERVER.
Comunque stoppando l'SQL Server l'engine SQL del linguaggio 4D continua a rispondere alle ricerche interne!

La porta TCP di accesso standard è la 1919, ma si può cambiare nelle preferenze.

Inoltre è possibile nello stesso pannello decidere i privilegi di accesso ai dati per gruppi di utenti:
Read Only =in sola lettura
Read/Write = in lettura e scrittura
Full = è possibile anche modificare la struttura del database

Novita' Novita' [v11 SQL] Nuovo valore assunto dalla variabile di sistema Document
La variabile di sistema Document, a partire dalla v11, conterrò sempre il percorso completo del documento.
Per essere più precisi, al comando:

docRef:=Create document("miodoc.txt")

nelle versioni precedenti avremmo ottenuto:

Document = "miodoc.txt"

Con la v11 SQL invece:

Document = "c:\Documenti\ miodoc.txt"

oppure

Document = "MacHD:Documenti:miodoc.txt"
Plugin Plugin Ottenere testo da 4D Write con WR Get text
Il comando WR Get text del plugin 4D Write permette di ottenere testo da un'area (tutta o una parte) di 4D Write.
Se ad esempio voglio importare un paragrafo di un'area Write scriverò il codice:

C_LONGINT($primo_l;$ultimo_l)
$aCapo:=Char(Carriage return )
WR Find (miaArea;$aCapo;0;0;0)
WR GET SELECTION (miaArea;$primo_l;$ultimo_l)
$testoriga:=WR Get text (miaArea;0;$ultimo_l-1)

Il numero massimo di caratteri che il comando ritorna è 32.000.

Già dalla versione 2004, e a maggior ragione con la v11 SQL, il comando WR Get text usa correttamente i caratteri Unicode.
Novita' Novita' [v11 SQL] Le nuove costanti per BLOB to text
BLOB to text permette di estrarre testo da un blob. Con l'avvento della v11 SQL con la gestione dell'Unicode, le costanti per il secondo parametro, textformat, sono cambiate. Adesso le costanti sono:


0 Mac C string
1 Mac Pascal string
2 Mac Text with length
3 Mac Text without length
4 UTF8 C string
5 UTF8 Text with length
6 UTF8 Text without length
Componenti Componenti [v11 SQL] Includere componenti all'interno della Build Application
Con la v11 SQL è possibile usare degli alias delle cartelle contenenti il component all'interno della cartella Conponents.

Quando si sceglie Build Application, è possibile scegliere quali component integrare nell'applicazione costruita: così, anche se la cartella Components della versione interprete contiene un alias, in fase di compilazione l'applicazione finale conterrà i componenti effettivi in modo da garantirne la trasportabilità.
Plugin Plugin Mettere i plugin dentro l'applicazione 4D
Per avere sempre i plugin disponibili ogni volta che si usa l'applicativo, sia nella versione 2004 che nella v11 è possibile includere la cartella Plugin.

In Windows si mette allo stesso livello dell'eseguibile, mentre su Mac occorre metterlo nel bundle: il modo più semplice è chiedere le informazioni (command-I) sull'applicazione e usare il pannello Plugin con cui è possibile aggiungere o togliere i Plugin.

Tecniche Tecniche [v11 SQL] Diverso funzionamento di USE SET
Con la nuova versione di 4D, il comando USE SET ha nettamente cambiato comportamento.

Se infatti, con le versioni precedenti, USE SET restituiva i record presenti nel set senza altre preoccupazioni, da adesso il comando restituisce un errore se uno dei record del set risulta cancellato nel frattempo.

Un'idea per aggirare l'ostacolo potrebbe essere quella presentata dal seguente codice, dove la necessità è quella di usare i record del set "TempSet" creato in precedenza:

ALL RECORDS
CREATE SET("RecordPresenti")
INTERSECTION("TempSet";"RecordPresenti";"TempSet")
USE SET("TempSet")
Codice Codice Quando non riesco a cambiare stampante con SET CURRENT PRINTER
Mi è capitato a volte che SET CURRENT PRINTER non mi cambiasse la stampante, soprattutto se la stampante era una stampante di rete. Per autorisolvermi il problema ho adottato alcune strategie.
Il primo tentativo, poco fruttuoso in realtà, è stato quello di pingare l'ip della stampante o del print server associato, usando:

C_LONGINT($alive)
For ($i;1;3)
      NET_Ping ([Stampanti]IP_da_controllare;"";$alive;1)
      If ($alive=1)
           $i:=5
      End if
End for

ma a volte la stampante non veniva variata ugualmente.
Allora sono passato all'approccio "controllo se la stampante è cambiata", cioè:

Repeat
      SET CURRENT PRINTER([Stampanti]Stampante)
      $StampanteCorrente:=Get current printer
Until ($StampanteCorrente=[Stampanti]Stampante)

Avendo poi visto che in massimo due/tre passaggi la stampante veniva settata, per evitare un controllo troppo bloccante ho deciso di usare:

For ($i;1;5)
      SET CURRENT PRINTER([Stampanti]Stampante)
End for


Così, se la stampante non viene settata per un qualsiasi motivo, stamperebbe comunque sulla stampante di default, ma questo errore non si è più verificato.
Tecniche Tecniche Aprire su Mac un file di backup spostato da Windows
Quando si sposta da un server Windows il file di backup per ripristinarlo su Mac, l'applicazione 4D potrebbe non riuscire a riconoscere il file perché mancano Tipo e Creatore.

Eseguire questo pezzo di codice per selezionare il file e assegnargli le informazioni corrette:

$ris:=Select document("";"";"";Use Sheet Window )
If (OK=1)
  SET DOCUMENT CREATOR(Document;"4D06")
  SET DOCUMENT TYPE(Document;"4DBK")
End if
Tecniche Tecniche Come preparare il database alla conversione alla v11 SQL
I database dalla versione 6.0 in poi sono convertiti automaticamente da 4D v11 SQL.
Se la versione della base dati è più vecchia è possibile prima convertirla alla 6.5: da notare che non devi avere una licenza per fare l'aggiornamento, basta la versione demo scaricata dal sito di 4d.

Ecco una serie di passi da seguire per fare la conversione:

Backup – Fai un backup completo della base dati.

Verifica con 4D Tools – Assicurarsi che struttura o base dati siano a posto; basta usare la corrispondente versione di 4D Tools. Se il database è a posto è meglio compattarlo. Se è danneggiato invece è meglio provare a ripararlo.

Disinstalla i componenti – I vecchi componenti devono essere disinstallati con il 4d Insider.

Verifica la compatibilità dei plugin – Sostituisci tutti i plugin 4D con la corrispondente versione 4D v11 SQL. Assicurarsi che gli i plugin non 4d siano compatibili con 4D v11 SQL: in linea di massima si possono usare i plugin utilizzati con la versione 2004. Per Mac sarebbe meglio richiedere la versione Universal dei plugin.

Assicurati di avere spazio su disco a sufficienza – 4D v11 SQL durante la conversione duplica tutti i file modificati facendo una copia di sicurezza di base dati e struttura.
Tecniche Tecniche Backup degli Utenti in un documento su disco
Già dalla versione 2004 è possibile fare un backup o trasferire gli utenti creati dall'Amministratore con i comandi USERS TO BLOB e BLOB TO USERS.

Ecco i due metodi da usare:

`Metodo Utenti_Registra
C_BLOB($utenti_blb)
USERS TO BLOB($utenti_blb)
$doc:=Create document("")
If (ok=1)
  CLOSE DOCUMENT($doc)
  BLOB TO DOCUMENT(Document;$utenti_blb)
  SHOW ON DISK(Document)
End if


`Metodo Utenti_Carica
C_BLOB($utenti_blb)
$doc:=Select document("";"*";"Seleziona il file di backup degli utenti";0)
If (ok=1)
  DOCUMENT TO BLOB(Document;$utenti_blb)
  BLOB TO USERS($utenti_blb)
End if
1
Tecniche Tecniche [v11 SQL] Modifica delle scorciatoie da tastiera
4D v11 SQL introduce nuove scorciatoia di tastiera, molte in allineamento con il normale uso nelle altre applicazioni. Ad esempio, Ctrl-P (win) o Cmd-P (mac) lanciano la stampa, mentre prima servivano ad aprire una procedura selezionata nel method editor.

Per chi volesse ripristinare alcune combinazioni a cui si è abituato è possibile seguire questa procedura:
- chiudere 4D
- identificare nella cartella 4D Extensions all'interno del package su Mac o allo stesso livello dell'eseguibile su Windows
- duplicare (per backup) il file 4DShortcuts.xml
- Cercare l'elemento che ha come attributo nome "OpenMethod"
- Cambiare l'attributo lettera da "K" a "P"
- Bisogna assicurarsi che una lettera non sia usata più volte: in questo caso la scorciatoia per "Print" dovrebbe essere cambiata da "P" a "K"
- Salvare il file
- Rilanciare 4D
Novita' Novita' [v11 - Conferenza] Le "Nested transaction"
Con la v11 SQL è possibile utilizzare le transazioni annidate. Il seguente codice è ammesso:

START TRANSACTION
  CREATE RECORD([Tabella1])
  START TRANSACTION
    CREATE RECORD([Tabella2])
    SAVE RECORD([Tabella2])
  VALIDATE TRANSACTION
  [...]
CANCEL TRANSACTION

Alla fine l'ultimo Cancel annulla anche la creazione del record in Tabella2.
1
Novita' Novita' [v11 - Conferenza] DISTINCT VALUES per avere la lista delle singole parole
Sappiamo che DISTINCT VALUES si usa per popolare un array con il contenuto di un campo indicizzato della selezione corrente.

Con la v11 SQL il codice:

ALL RECORDS([Testo])
ARRAY STRING(80;arrTesto;0)
DISTINCT VALUES([Testo]Informazioni;arrTesto)

se il campo [Testo]Informazioni è indicizzato per parola chiave (keyword index), DISTINCT VALUES popola l'array arrTesto con le singole parole che compongono i testi del campo [Testo]Informazioni.

È inoltre interessante notare che il comando lavora in modo coerente con la selezione. Con la leggera variante al codice precedente:

ALL RECORDS([Testo])
REDUCE SELECTION([Testo];1)
ARRAY STRING(80;arrTesto;0)
DISTINCT VALUES([Testo]Informazioni;arrTesto)

(aggiungendo REDUCE SELECTION) l'array viene popolato con le parole dell'unico record che rappresenta la selezione attuale.
Novita' Novita' [v11 - Conferenza] Uso di Project Form e DIALOG
I project form sono indipendenti dalla tabella:

  • è la posizione preferibile per tutte le maschere di dialogo indipendenti dai dati;
  • è possibile trascinare i form da una tabella all'altra o spostarli sotto i Project Form;
  • quando si cancella una tabella i relativi form automaticamente si spostano fra i Project form;

Il comando DIALOG adesso può ricevere come form anche una project form, che vengono passate al comando semplicemente col loro nome (senza il primo parametro che indica la tabella).

Plugin Plugin [v11 - Conferenza] Futuro di 4D Draw
4D Draw, il plugin di 4th Dimension dedicato al disegno, è destinato a scomparire.

Il plugin non è Universal Binary, e dunque per essere usato il database sotto Macintosh dovrà essere utilizzato con Rosetta.

Il suggerimento di 4D è di usare le nuove funzionalità grafiche SVG incluse nella nuova versione.
Comandi Comandi [v11 SQL] Nuovo comando Choose
4D v11 SQL ha ora un nuovo comando Choose.

Invece di scrivere un lungo case per avere un valore in funzione di un parametro singolo, è possibile usare questo comando in una singola riga.

Valore:=Choose(Criterio;Valore1;Valore2;..)

-Se il Criterio è Boolean, Choose ritorna Valore1 se True o Valore2 se False. In this case, the command expects exactly three parameters: criterion, value1 and value2.
-Se il Criterio è un numero, Choose ritorna il valore corrispondente, partendo dal Valore1 per lo Zero.

Ad esempio:
Lavoro:=Choose([Anagrafica]Mansione;"CEO";"Ingegnere Software";"Barista Starbucks";"Attore")

Questo codice è esattamente equivalente a:

Case of
  :([Anagrafica]Mansione =0)
      Lavoro:="CEO"
  :([Anagrafica]Mansione =1)
      Lavoro:="Ingegnere Software"
  :([Anagrafica]Mansione =2)
      Lavoro:="Barista Starbucks"
  :([Anagrafica]Mansione =3)
      Lavoro:="Attore"
End case
Info Info Evoluzione di 4D Pack: AP ShellExecute
4D v11 SQL dovrebbe essere l'ultima versione di 4D in cui sarà ancora possibile utilizzare il comando del 4D Pack AP ShellExecute.

4D informa infatti che nelle future versioni di 4D non sarà più presente questo comando, egregiamente sostituito dal comando LAUNCH EXTERNAL PROCESS presente nel linguaggio di programmazione di 4th Dimension.

Per l'uso di LAUNCH EXTERNAL PROCESS rimandiamo a questa faq.

Plugin Plugin [v11 SQL] Controllare l'esistenza di un metodo con AP Does method exist
Il comando AP Create method (presente già nel 4D Pack) consentiva di creare metodi all'interno di una struttura non compilata. Mancava però la possiibilità di controllare se un metodo con il nome specificato esistesse.

A questa lacuna pone riparo il comando del 4D Pack AP Does method exist che prende come parametro una stringa e restituisce 0 se il metodo non esiste, 1 altrimenti.

Comandi Comandi [v11 SQL] Uso di SELECT
SELECT è il comando del linguaggio SQL che permette di ottenere dei dati. Ecco un sunto della sintassi che può avere:


SELECT [ALL | DISTINCT]
{* | campo_di_ricerca, ..., campo_di_ricerca}
FROM tabella_di_ricerca, ..., tabella_di_ricerca
[WHERE condizioni_di_ricerca]
[ORDER BY lista_di_ordinamento]
[GROUP BY lista_di_raggruppamento]
[HAVING condizioni_di_ricerca]
[LIMIT {numero_intero | ALL}]
[OFFSET numero_intero]
[INTO {4d_language_reference, ..., 4d_language_reference | LISTBOX 4d_language_reference}]
[FOR UPDATE]

Info Info Compatibilità con Crystal Reports
Usando Crystal Reports® con 4D è possibile avere dei problemi per i report che usano dati provenienti da più tabelle. Questo perché Crystal Reports® "non sa" come utilizzare le join all'interno e all'esterno di 4D.

Per risolvere questo problema basta cliccare, all'interno del DSN che gestisce l'origine ODBC per Crystal Reports, il pulsante "Crystal Reports® Compatibility setup...". Scelto "OK" e riavviata la macchina il problema sarà risolto.

Tale pulsante non fa altro che aggiungere alcune chiavi nel registro di sistema (del tipo NoOuterJoinEscSeq=ODBC4D).

Tali chiavi sono necessarie sia per Crystal Reports® v10 che per Crystal Reports® v11.

Codice Codice [v11 SQL] Elenco dei 4D server aperti con i nuovi comandi UDP
UDP (User Datagram Protocol) è un protocollo di comunicazione di facile implementazione più snello di TCP (per l'header TCP usa 20 byte, UDP 8) ma non altrettanto affidabile. Se da un lato infatti permette comunicazioni veloci, dall'altra non viene fatto alcun controllo d'errore o riparazione di dati non ricevuti.

La v11 SQL mette a disposizione una serie di comandi che implementano la comunicazione via UDP. Quello che segue è un esempio che usa i comandi UDP per ottenere l'elenco dei server 4D presenti in una rete locale.



ARRAY STRING (255;asHost;0)
ARRAY STRING (32;asMachineName;0)
ARRAY STRING (32;asService;0)
ARRAY STRING (32;asDBName;0)
C_BLOB ($Blob)

$indirizzo_t:="255.255.255.255"
$Porta_t:=19813
$posizione_l:=32
SET BLOB SIZE($Blob;96;0)
TEXT TO BLOB("4D Server";$Blob;Mac text without length;$posizione_l)
$Err:=UDP_New(0;$udpID)
$Err:=UDP_SendBLOBTo($udpID;$indirizzo_t;$Porta_t;$Blob)
$Secondi_l:=2
$Timeout_l:=Milliseconds+($Secondi_l*1000)
Repeat
  DELAY PROCESS(Current process;6) `... espresso in ticks
  SET BLOB SIZE($Blob;0;0)
  $guardaIndirizzo_t:=$indirizzo_t
  $Err:=UDP_ReceiveBLOBFrom($udpID;$guardaIndirizzo_t;$Porta_t;$Blob)
  If (BLOB size($Blob)>0)
    $posizione_l:=0
    $Host_t:=BLOB to text($Blob;Mac C string;$posizione_l;32)
    $posizione_l:=32
    $Service_t:=BLOB to text($Blob;Mac C string;$posizione_l;32)
    $posizione_l:=64
    $DBName_t:=BLOB to text($Blob;Mac C string;$posizione_l;32)
    $Pos:=Find in array(asMachineName;$Host_t)
    If ($Pos>0)
      APPEND TO ARRAY(asHost;$guardaIndirizzo_t)
      APPEND TO ARRAY(asMachineName;$Host_t)
      APPEND TO ARRAY(asService;$Service_t)
      APPEND TO ARRAY(asDBName;$DBName_t)
    End if
  End if
Until ((Milliseconds>$Timeout_l) | ($Err#0))
$Err:=UDP_Delete($udpID)

Codice Codice [v11 SQL] Ottenere l'MD5 di un file con AP Get file MD5 digest
L'algoritmo MD5 (Message Digest 5) è una funzione di hash usata per la criptazione dei dati

Il comando del 4D Pack AP Get file MD5 digest permette di ottenere il digest MD5 per un certo file.

L'uso tipico del comando potrebbe essere il seguente:

C_TEXT($thedoc)
C_TEXT(<>digest)
C_LONGINT($resfork)
$resfork:=0 `su Mac: 0 specifica su data fork, 1 su resource fork
$thedoc:=Select document
$error:=AP Get file MD5 digest($thedoc;<>digest;§resfork)

Novita' Novita' [v11 SQL] Il comando COMPONENT LIST
Il comando COMPONENT LIST, che prende come parametro un array, popola quest'ultimo con i nome dei components caricati dal database corrente.

Il comando può essere chiamato sia dal database principale che dai componenti stessi.

L'array viene riempito con i nomi completi dei file di riferimento (.4db, .4dc or .4dbase).

Se non sono presenti componenti, l'array viene restituito vuoto.

Comandi Comandi [v11 SQL] Il comando QUERY BY SQL
Avendo al proprio interno un motore nativo SQL, 4D v11 permette di utilizzare il comando QUERY BY SQL per eseguire query su database 4D utilizzando la sintassi SQL. Ad esempio:

QUERY BY SQL([Employees];"name=’smith’")

equivale alla query SQL:

SELECT * FROM Employees WHERE "name=’smith’"

in maniera molto simile all'uso di QUERY BY FORMULA.
QUERY BY FORMULA non usa le relazioni definite nella struttura del database, le join devono invece essere definite all'interno della query. Ad esempio, se la struttura è:

[PEOPLE]
Name
City
[CITIES]
Name
Population

la query sarà:

QUERY BY FORMULA([PEOPLE]; [CITIES]Population>1000)

oppure

QUERY BY SQL([PEOPLE];"people.city=cities.name AND cities.population>1000")
Componenti Componenti [4D Pop] Color Chart
Un altro componente della seria 4d Pop che mostra una palette grafica dove scegliere un colore e avere il codice 4d per usarlo nei propri metodi.

Faq

Il componente lo potete scaricare da qui:
ftp://ftp2-public.4d.fr/demos/francais/multiplateforme/bases_exemples/v11/composants/4DPop%20Color%20Chart.4dbase.zip

Open Source
Il component è fornito compilato, ma nella cartella è disponibile anche una cartella "Sources" con i sorgenti.



Componenti Componenti [4D Pop] Bookmark
Un altro componente della seria 4d Pop che presentiamo serve a raccogliere una propria lista di indirizzi web.

La lista è riordinabile con il drag e drop, editabile con il doppio clic, si possono aggiungere indirizzi trascinandoli dal Finder, dal browser o dal programma di Mail, da qualsiasi programma di testo.

Il componente lo potete scaricare da qui:
ftp://ftp2-public.4d.fr/demos/francais/multiplateforme/bases_exemples/v11/composants/4DPop%20Bookmarks.4dbase.zip

Open Source
Il component è fornito compilato, ma nella cartella è disponibile anche una cartella "Sources" con i sorgenti.


Componenti Componenti [4D Pop] Migration tools
Il primo componente della seria 4d Pop che presentiamo è utilissimo per chi passa alla versione v11; contiene:

1. Shortcut Editor, con cui è possibile personalizzare le scorciatoie da tastiera di 4d (che sono tutte rinnovate), ma soprattutto sono presenti due menu per impostare tutte le scorciatoie simili alla 2004 oppure per ripristinare tutte quelle di default della v11.

2. Migrate constants: Per chi ha definito delle costanti private nella struttura di 4d usando le risorse 4DK#, questo comando crea un plugin apposta trasferendo tutti i dati necessari.

3. Migrate the macros: questo strumento riformatta tutte le macro che diano errori nel formato della nuova versione

Il componente lo potete scarica da qui:
ftp://ftp2-public.4d.fr/demos/francais/multiplateforme/bases_exemples/v11/composants/4DPop%20migration.4dbase.zip

Open Source
Il component è fornito compilato, ma nella cartella è disponibile anche una cartella "Sources" con i sorgenti.

Componenti Componenti [4D Pop] La palette per gli sviluppatori
4D ha messo a disposizione un interessante componente, 4D Pop, scaricabile dall'indirizzo:

ftp://ftp2-public.4d.fr/demos/francais/multiplateforme/bases_exemples/v11/composants/4DPop.4dbase.zip

Questo component mette a disposizione tutti i componenti installati nella struttura all'interno di una palette, chiamata 4D Pop appunto.

Il component si installa inserendo il database 4DPop nella cartella Components da creare nella cartella della struttura del nostro software, e nell'"On Statup Database Method" basta scrivere "Install 4DPop" e battere il tab per lanciare una macro che ci scriverà il seguente codice:

If (Not(Is compiled mode))
  ARRAY TEXT($tTxt_Components;0)
  COMPONENTS LIST($tTxt_Components)
  If (Find in array($tTxt_Components;"4DPop")>0)
    EXECUTE METHOD("4DPop_Palette")
  End if
End if

Open Source
Il component è fornito compilato, ma nella cartella è disponibile anche una cartella "Sources" con i sorgenti.

Esistono inoltre tutta una serie di plugin compatibili 4DPop di cui discuteremo nelle prossime faq.

Componenti Componenti [v11 SQL] Componenti
Da questa versione la creazione e la gestione dei componenti è molto più facile e diretta.

Creazione: un componente è una struttura generica, chiamata Matrix; per ogni metodo si può indicare se è richiamabile dal programma in cui è usato come componente ( Host )

Maschere: possono essere condivise solo le maschere del progetto, cioé le Form slegate dalle tabelle.

Installazione: basta copiare la struttura ( oppure il solo Alias !) in una cartella Components

Il componente può essere compilato; in questo caso può essere utilizzato anche in strutture interpretate.

Quindi rispetto alle versioni precedenti:
1. non serve più l'Insider
2. non si devono più aggiungere tabelle o form a tabelle del programma ospite
3. Le variabili sono proprie del componente e non si possono sovrapporre a quelle del programma ospite; l'unico modo per accedere alle variabili è usare i puntatori.
Info Info Limiti di 4D: tipi per variabili e campi *
Ecco i limiti di 4D 2003 per i vari tipi di dati:

Date: dal 1/1/100 al 31/12/32.767;
Time: da 00:00:00 a 596.000:00:00;
Blob: fino a 2Gb;
Text: 32.000 caratteri;
Longint: da (-2^31) a (2^31)-1;
Real: ±1.7e±308 (15 cifre).

Nella versione v11 SQL sono modificati o aggiunti questi tipi:

Alpha: 255 caratteri;
Text: 2 miliardi di caratteri (2GB);
Integer 64 bit: da (-2^63) a (2^63)-1;
Float: numero reale senza arrotondamenti e conseguente perdita di precisione
Info Info Limiti di 4D: la struttura *
Ecco elencati i limiti per una struttura 4D fino alla 2004:

Data file: 127 segmenti da 2Gb ciascuno;
Tabelle: 255;
Campi: 511 per tabella;
Record: 16 milioni di record per tabella;
Indici: 16 milioni di chiavi;
Forms; 32.000;
Metodi: 32.000 (fino a 2Gb per metodo);
Liste: 8.000 item per lista;
Sicurezza: 16.000 utenti e 16.000 gruppi;
Processi: 32.767 contemporaneamente.

Dalla versione v11 SQL i limiti aggiornati di cui abbiamo notizia sono i seguenti:

Data file: dimensione del file illimitata (il limite può dipendere dal sistema operativo)
Tabelle: 32.767;
Campi: 32.767 per tabella;
Record: 1 miliardo di record per tabella;
Indici: 128 miliardi di chiavi per tabella.
Comandi Comandi [v11 SQL] Select folder Usa una cartella di partenza
Nelle precedenti versioni di 4D, il comandoSelect folder permetteva di scegliere una cartella a partire dalla root del sistema. Ciò risultava molto noioso se la cartella da raggiungere era particolarmente annidata.

Per migliorare la fruibilità del comando, nell'ultima versione del tool di sviluppo è possibile passare come secondo parametro (opzionale) o una stringa vuota (e in tal modo verrà di default mostrata la cartella dei docimenti impostata sul sistema), oppure il percorso della cartella da mostrare all'apertura della finestra di scelta.

E' possibile passare anche un intero (fino a 32000) che indica un determinato percorso memorizzato da 4d e riproposto quando viene usato lo stesso numero, così come succedeva con il comando Select Document. I numeri passati sono condivisi dai due comandi.
Codice Codice [v11 SQL] Scegliere il tipo di indici
Dalla versione v11 è ora possibile scegliere fra diversi tipi di indici:

Automatic : il sistema seleziona quello più adatto

B-tree : in pratica i classici indici ad albero da sempre usati e adatti più o meno a tutti i casi generali

Cluster b-tree : è molto efficiente per i camp che non hanno molte varianti, come possono esserlo i booleani o il campo Sesso

Keyword index : è un secondo indice che è possibile aggiungere ai campi alfabetici e testo; velocizza le Ricerche per parola chiave

Composite: serve ad ottimizzare ricerche che vanno fatte spesso su più campi, come ad esempio Nome e Cognome oppure Prefisso e Numero di Telefono
Plugin Plugin Prestazioni di 4DWrite
Il linguaggio del plugin 4D Write simula le azioni da eseguire su una certa area. E' possibile creare aree "virtuali" chiamate offscreen area che è possibile creare e cancellare in ogni momento.

E interessante notare che, dal punto di vista prestazioni, è molto meglio, quando possibile, cancellare il contenuto di un'area e riutilizzarla piuttosto che cancellarla per poi ricrearla (ad esempio all'interno di un ciclo).

Info Info [v11 SQL] Personalizzare l'icona della finestra di login
E' possibile personalizzare l'icona della finestra di login del database, dove si inserisce utente e password.

Per default, l'icona è il logo di 4D: per sostituirla, basta mettere un'immagine chiamata LoginImage.png nella cartella Resources del database, dentro la cartella .4dbase vicino alla struttura.

Il file deve essere nel formato "png" con una dimensione di 80x80 pixels
Codice Codice [v11 SQL] Requisiti minimi
Ecco la lista dei requisiti minimi per la v11. La cosa che risalta maggiormante è la necessità di uno schermo con risoluzione minima 1280x1024 (per lo sviluppo almeno, visto che il pulsante "Preferences" del Designer "deborda" in uno schermo 1024x768).

Ecco il dettaglio:

Windows
Pentium III
Windows Vista
Windows XP
512 MB RAM (1 GB raccomandato)
Risoluzione dello schermo 1280x1024

Mac OS
Mac Intel ® or PowerPC (G5 raccomandato)
Mac OS 10.4.5 o successivo
512 MB RAM (1 GB raccomandato)
Risoluzione dello schermo 1280x1024
Codice Codice [v11 SQL] Cercare duplicati usando il codice SQL
Inn SQL si possono risolvere alcune cose che in 4D sono lunghe e tediose, tipo cercare quanti duplicati ci sono in una tabella di record anagrafici.

Ecco un esempio di ricerca in Sql:

ARRAY LONGINT(arrayQuanti;0)
ARRAY TEXT(arrayRagSoc;0)
ARRAY TEXT(arrCitta;0)
ARRAY TEXT(arrIndirizzo;0)

Begin SQL
        SELECT count(RagioneSociale), RagioneSociale, Citta, Indirizzo
        FROM Anagrafica
        GROUP BY RagioneSociale, Citta, Indirizzo
        HAVING count(RagioneSociale)>1
        INTO :arrayQuanti, :arrayRagSoc, :arrCitta, :arrIndirizzo
End SQL

La select cercherà nella tabella [Anagrafica] i record che abbiano gli stessi dati nei campi [Anagrafica]RagioneSociale, [Anagrafica]Citta, [Anagrafica]Indirizzo.

Poi produrrà 4 colonne di dati e cioè il conteggio e i tre campi con i dati unici.

Il risultato andrà negli array elencati in coda nello stesso ordine dei campi della prima riga.
Tecniche Tecniche [v11 SQL] Variabile degli oggetti nelle maschere
Le variabili associate ad un oggetto in una form può ora contenere non solo il nome di una variabile, ma anche direttamente una qualsiasi espressione o funzione in linguaggio 4d.

Ad esempio, un oggetto picture può contenere la variabile
pictFoto
  
oppure il campo
[utente]foto

oppure ancora la funzione:
dammiFoto([utente];"formatopiccolo")

Codice Codice [v11 SQL] Passaggio di parametri ai comandi SQL
Per passare un valore dinamico fra il linguaggio di 4d e l'SQL, si possono indicare i nomi delle variabili o in due coppie di segni minore/maggiore o prefissarli con un due punti, ad esempio:

<>
:miaListBox

La notazione con i due punti rappresenta un parametro per una Parameterized Query.
La notazione con i segni di maggiore/minore viene chiamata Direct Association.

Ecco due esempi:
`Mostra il risultato di una select in una List Box, miaListBox.
Begin SQL
      SELECT * FROM Angarafica INTO <>
End SQL

`Inserisci un determinato valore in una chiamata
sNome:="Mario"
Begin SQL
      SELECT * FROM Anagrafica WHERE Nome = :sNome
End SQL
Codice Codice [v11 SQL] Dove si trova l'insider?
L'insider non c'è più come applicativo a parte e le sue funzioni sono ora incorporate.. ad esempio:

  • i componenti si installano trascinandoli nella cartella Components
  • il find nel database oltre a sembrare più veloce ha un sacco di opzioni in più
    (ad esempio puoi cercare fra gli oggetti modificati dopo una certa data!)
  • se cerchi solo fra i metodi il risultato appare su due colonne con i chiamanti e i chiamati
  • anche nell'explorer il menu contestuale su un metodo ti permette di mostrare chi lo chiama
  • è possibile spostare gli oggetti fra due database, aprendo due 4d Developer: durante la copia 4d porta tutti gli oggetti relativi e in una finestra di scelta permette di decidere cosa fare di eventuali duplicati

Codice Codice [v11 SQL] Campi testo, BLOB e immagini
In 4D v11, i campi testo, BLOB e immagini che possono contenere fino a 2gb di dati
sono registrati fuori dal record stesso: questo aumenta la velocità del database, specialmente durante le ricerche: quando 4d accede al record non carica in memoria tutti i dati contenuti in questi campi. Sono invece caricati automaticamente quando il record cercato viene trovato.

L'operazione non richiede nessuna modifica al codice esistente, se non che rende inutile eventuale codice impostato per registrare questi campi in una tabella separata: questa tecnica funziona ancora, ma con la v11 non serve più.
Codice Codice [v11 SQL] Informazioni sugli oggetti delle form
Durante lo sviluppo di una form, esiste una nuova scorciatoia per avere delle informazioni veloci (nome, coordinate, etc) su un oggetto qualsiasi.

Per vederle basta tenere premuto la combinazione di tasti Ctrl+Shift (su Windows) o Command+Shift (su Mac) puntando sull'oggetto con il mouse.
Novita' Novita' [v11 SQL] Come gestire i numeri di tabella e di campo
La possibilità di cancellare tabelle e campi pone un problema con la gestione di Count tables e Count fields, i comandi che permettevano di conoscere le dimensioni di riferimento nella struttura.

I due comandi sono stati adesso modificati in Get last table number e Get last field number, che permettono di conoscere gli ultimi numeri di tabella e di campo usati. Visto però che alcune tabelle o campi potrebbero essere stati cancellati, si dovranno usare anche i nuovi comandi Is table number valid e Is field number valid, che ritornano True se tabella o campo non sono stati cancellati.

Un ciclo completo sulla struttura diventa dunque:

For($thetable;1;Get last table number)
      If (Is table number valid($thetable))
           For($thefield;1;Get last field number($thetable))
           If(Is field number valid($thetable;$thefield))
           ... `il campo esiste ed è valido
           End if
           End for
      End if
End for

Codice Codice [v11 SQL] Nuove proprietà delle ListBox
Le Listbox sono degli oggetti che permettono di mostrare nelle maschere griglie di dati.

Dalla versione v11 le ListBox possono essere collegate (anche da linguaggio di programmazione) alla selezione corrente di una tabella o ad una named selection (in pratica una selezione salvata con un suo nome).

Quando è collegata alla selezione corrente una listbox editabile aggiorna contemporaneamente i dati e riceve automaticamente gli aggiornamenti.

Un'altra cosa interessante la possibilità di riempire al volo una listbox con il risultato di una select:

Begin SQL
    select * from Anagrafica into :Listbox
End SQL

Questo codice riempirà automaticamente la listbox con i dati dalla tabella Anagrafica eventualmente creando le colonne mancanti o rendendo invisibile le colonne di troppo.
Codice Codice [v11 SQL] Ricerca per parola chiave
E' disponibile un nuovo comando che effettua la ricerca di una "parola" intera in un campo testo.

Nella finestra delle Query l'operatore si chiama “contains keyword”, mentre nel linguaggio (nei comandi QUERY) si può usare il carattere % .

La funzione trova solo singole parole che nel testo siano separate dalle altre da spazi o punti, virgole, etc.

La ricerca è indifferente alle maiuscole e ai caratteri diacritici, tipo l'accento sulle vocali) e rispetta la @ come wildcard.
Info Info [v11 SQL] Da dove posso scaricare 4d ?
La versione di 4D v11 SQL è scaricabile principalmente tramite un installer di piccole dimensioni che scarica dai siti americano o francese l'ultima versione disponibile di quello che si seleziona.

Questa è la modalità consigliata e dovrebbe essere più sicura sia come aggiornamento che per velocità (alcune componenti interne ripetute sono scaricate una volta sola).

Gli Online installer si trovano qui:
http://www.4d.com/products/downloads/download-v11.html

Da notare che l'installer tiene in locale quello che è già stato scaricato.. quindi la sua dimensione dovrebbe automaticamente crescere. Alle successive installazioni dovrebbe scaricare solo le componenti eventualmente aggiornate nel frattempo.

Per chi invece volesse installare 4d su più macchine ottimizzando quindi il download, esiste la possibilità di scaricare i file completi, da qui:
http://www.4d.com/products/downloads/download-v11-FTP.html

I file completi sono circa 580MB nella versione Windows multilingua, e 180MB nella versione Inglese per Mac.


1
Comandi Comandi [v11 SQL] USE EXTERNAL DATABASE: query SQL esterne nel codice 4D
Il comando USE EXTERNAL DATABASE, la cui sintassi è

USE EXTERNAL DATABASE (sourceName{; user; password})

permette di utilizzare il motore SQL di 4D (e le relative query) per accedere a dati presenti in DSN definiti nel sistema.

Usando questo comando, tutte le successive chiamate del processo del tipo Begin SQL/End SQL saranno indirizzate al database sourceName, finché non viene utilizzato il comando USE INTERNAL DATABASE o un altro USE EXTERNAL DATABASE per accedere ad altri DSN (simile, per capirci, al funzionamento di SET QUERY DESTINATION).

Ecco un esempio:


   C_TEXT(sourceNamesArr;sourceDriversArr;0)
      GET DATA SOURCE LIST(1;sourceNamesArr;sourceDriversArr) `i data source utente
      If (Find in array(sourceNamesArr;"emp")#-1) `se emp esiste
           USE EXTERNAL DATABASE("emp";"tiger";"scott")
           Begin SQL
           ... `stringhe SQL
           End SQL
      End if

Codice Codice Ingrandire un'immagine in una variabile
Se avete una variabile immagine in un form, potete zoomarla (ingrandirla o rimpicciolirla) moltiplicando la variabile per un numero maggiore o minore di 1.

Ad esempio:

immagineNelForm:= immagineNelForm * 1.1 `per ingrandirla del 10%

immagineNelForm:= immagineNelForm * 0.9 `per rimpicciolirla del 10%

Info Info Collegamento a SQL Server da Mac OS X
Ci sono tre possibilità di driver ODBC per Microsoft SQL Server per Mac OS X:

OpenLink Software dal costo di 99$

Actual Technologies dal costo di 29$

FreeTDS gratis, ma bisogna ricompilarlo su mac e la versione 0.64 ha qualche problema nell'esecuzione dei comandi tipo SQLTables (che ritorna la lista delle tabelle).

Questi driver sono tutti basati sulla libreria (o framework) chiamata iODBC, che si trova inclusa nelle installazioni dei primi due e deve essere installata a parte nel terzo caso oppure quando bisogna aggiornarla rispetto a quelle preinstallate.

Per ottenere la sola libreria iOdbc (che è gratuita) già compilata per Mac OS X si può scaricare dal sito OpenLink solo l'opzione SDK che ha appunto l'ultima versione, attualmente la 3.52.5.

Ad esempio, la versione attuale dell'Actual Technologies (v. 2.7), installi iOdbc 3.52.2 che dà degli errori durante l'uso del driver (dà la lista delle tabelle, ma non carica i dati).
Bug Bug Bug del Check Syntax in Client Server 4D 2004 Mac
Se in una installazione client server ad un certo punto non riuscite a fare più il check syntax, provate a ridurre il nome della struttura.

In pratica sembra che il nome della cartella delle preferenze debba essere minore di 32 caratteri: il nome è costruito come nome + indirizzo ip + porta, nel caso in cui serva una porta diversa. Con "_" al posto degli spazi, cosi:

NOMESTRUTTURA.4DB_123_123_123_123_19815


Novizi Novizi Convertire un'applicazione da Macintosh a Windows
In linea di massima, 4d in versione windows apre la struttura e i dati creati su Mac.

In particolare, ecco alcuni punti da verificare:

1. Struttura dei file

Se la versione di 4d è la 2003 occorre splittare i file Mac separando il documento dalle risorse, con le estensioni finali 4DB e RSR per la struttura e 4DD e 4DR per i dati, usando l'applicativo 4D Transporter (gratuito, scaricabile dal sito di 4d, funziona su Mac).

Se il programma è invece stato creato con la 2004 dovrebbe essere già splittato.

2. Plugin

Se la versione di 4d è la 2003 ci potrebbe essere allo stesso livello della struttura una cartella Mac4DX con dei plugin di comandi aggiuntivi; occorrerà avere una cartella Win4DX e controllare che ci siano gli stessi plugin in versione windows.

Nella versione di 4D 2004 le cartelle Mac4DX e Win4DX sonos sostituite dalla cartella Plugins che contiene una versione dei plugin multipiattaforma, per cui in questo caso non ci sarebbe niente da fare.

3. Estetica

Nella versione per Mac potrebbero essere stati usati font e dimensione di campi non perfettamente riproducibili in automatico su Windows. Ci sono varie tecniche per assegnare font in automatico (tipo usando gli stili), ma dipende da come è stato disegnato il programma. In generale, bisogna fare un giro nelle varie maschere a verificarne la leggibilità.


Plugin Plugin Taglia e incolla con 4DWrite
Per effettuare le operazioni di taglia e incolla con 4DWrite si possono seguire due strade.
La prima è quella di usare

WR EXECUTE COMMAND (Temporary;wr cmd cut )

e

WR EXECUTE COMMAND (Temporary;wr cmd paste )

per effettuare le operazioni tramite gli appunti.
L'altra possibilità è usare:

$myTempBlob:=WR Get styled text (Temporary)

e

WR INSERT STYLED TEXT (Temporary;$myTempBlob)

Questo metodo consente il trasferimento del testo con i suoi attributi (colore, stile), eccettuato i nomi degli stili e i dati sui paragrafi (margini, tabulazioni, ecc).

Codice Codice Contare le parole di un testo
Ecco un piccolo metodo per contare il numero di parole presenti in un testo passato come parametro. In maniera banale si contano gli spazi non consecutivi, inserendo inoltre le eccezioni che servono (tipo presenza di trattini).

C_LONGINT($numeroparole;$posizione)
C_TEXT($1)

$numeroparole:=0
If (Length($1)>0)
      For ($posizione;1;(Length($1)-1))
          If (($1[[$posizione]]=Char(32)) & ($1[[$posizione+1]]#Char(32)) & ($1[[$posizione+1]]#"-"))
           $numeroparole:=$numeroparole+1
          End if
      End for
      $numeroparole:=$numeroparole+1
End if
$0:=$numeroparole


2
Codice Codice Print one job (Print Record+ Print form)
Per ottenere un unico documento dalla stampa di un record, magari anche multipagina, ed un allegato (utile per invio di Fax) inserire nel metodo del Form

If (b_DaEseguire)
    Print form([TABLE];"Allegato_A")
    PAGE BREAK
    b_DaEseguire:=False
End if

mentre il metodo chiamante sara' quello standard per stampe

OUTPUT FORM([TABLE];"Form")
b_DaEseguire:=True
PRINT RECORD([TABLE])

verificare che sia abilitato il selettore 'On Header' del Form del Record
(purtroppo viene stampato per primo l'allegato)
Codice Codice Integrare Ajax Framework con una gestione web esistente
Se avete già una gestione delle pagine Web sul vostro applicativo 4D, quando installate Ajax Framework della 4d Web Pack 2.0, oltre alle istruzioni indicate nella documentazione, modificate così il metodo generale della On Web Connection:

` On Web Connection

C_TEXT($1;$2;$3;$4;$5;$6)
If ($1="/DAX/@")
  DAX_Dev_OnWebConn($1;$2;$3;$4;$5;$6)
Else

` qui va il codice preesistente

End if

Info Info Impedire la connessione ODBC
Se si vuole impedire l'accesso via ODBC ad un particolare database 4th Dimension, ad esempio perché ci sono più server attivi, ma solo alcuni devono essere visibili via ODBC, è possibile negare questo tipo di connessione.

Per fare ciò bisogna deselezionare la voce "Allow 4D Open Connections" nelle database properties in questo modo:
- aprire il database via 4D Client;
- da Design aprire le proprietà del database;
- nella pagina "Data control and access" deselezionare "Allow 4D Open connection".

La modifica avrà effetto a partire dal successivo riavvio di 4D Server.


1
Plugin Plugin ODBC: ridurre il numero di righe ritornate da una query SQL
E' possibile ridurre il numero di record restituiti da una query SQL utilizzando la costante ODBC MAX ROWS all'interno del comando ODBC SET OPTION. Ecco un esempio:

ODBC LOGIN("TestODBC";"";"")
SQLStmt:="SELECT * FROM Clienti"
ODBC SET OPTION(ODBC Max Rows ;100)
ODBC EXECUTE(SQLStmt;[Result1]Field1;[Result1]Field2)
ODBC LOAD RECORD(ODBC All Records )
Novizi Novizi Numero di record limitato o illimitato
Quando si usa il comando SET QUERY LIMIT per limitare il numero di record ritornati da una query, è necessario reimpostare subito dopo il numero di record ritornati a "illimitato", usando

SET QUERY LIMIT(0)

Altrimenti, infatti, il numero di record ritornati dalle query all'interno del processo corrente sarà sempre limitato.
Info Info Windows: Aprire i file PTH col 4D Client corretto
I file con estensione "PTH" (cioè PaTH, percorso) sono i file che contengono le informazioni di connessione ad un certo server. Se sulla nostra rete esistono più server è possibile creare collegamenti automatici tra un 4D Client e il file PTH da usare con quel client.
Per creare l'associazione correttamente:
- creare un collegamento al client da usare;
- aprire le proprietà del collegamento
- aggiungere alla riga "Destinazione" delle virgolette doppie prima e dopo il percorso del 4D client;
- alla riga così ottenuta aggiungere uno spazio e poi, sempre tra virgolette, il percorso del file PTH salvato.

Il collegamento così creato userà il file PTH impostato.
Codice Codice Un file col contenuto della cartella
Il seguente metodo salva in un file di testo l'elenco dei file contenuti in una cartella.

$folder:=Select folder("Scegli la cartella")
If (OK=1)
      ARRAY TEXT($arrelenco;0)
      DOCUMENT LIST($folder;$arrelenco)
      $testo:=""
      SORT ARRAY($arrelenco;>)
      For ($i;1;Size of array($arrelenco))
           $testo:=$testo+$arrelenco{$i}+Char(Carriage return )+Char(Line feed )
           C_BLOB($blob)
           TEXT TO BLOB($testo;$blob;3)
           BLOB TO DOCUMENT("elenco.txt";$blob)
      End for
End if
Codice Codice Bloccare record multipli di una tabella in un processo
Quando carichi (con il load record o il next record, ad esempio) un record in modalità scrittura, 4d lo blocca automaticamente. Il blocco è di un solo record per tabella in ogni processo.

Se vuoi bloccare più record, puoi usare il comando PUSH RECORD.

Questo comando mette il record corrente in uno stack, per poi recuperarlo con un successivo POP RECORD: viene usato normalmente per tenere da parte un record mentre si fanno delle altre query sulla stessa tabella.

Ma la cosa meno nota è che si possono "pushare" anche più record uno dopo l'altro e restano tutti bloccati in scrittura finché non vengono "poppati". I record vengono ritrovati nella modalità LIFO, l'ultimo inserito è il primo ritrovato.
Bug Bug [Risolto] Installer 2004.3 dopo l'installazione della 2004.1
Grazie ad una news pubblicata da 4DToday qualche settimana fa, ho potuto installare 4D 2004.3 su una macchina dove erano stati installati tutti i 4D 2004 precedenti (compresa la versione 2004.1), risolvendo un problema legato a InstallShield. All'inizio dell'installazione della 2004.3, infatti, l'installer segnala un errore 6001.
Per risolvere "manualmente" questo problema è sufficiente rimuovere la DLL nel percorso:

C:\Programmi\File comuni\InstallShield\Professional\RunTime\0701\Intel32\setup.dll

La cartella potrebbe essere nascosta, e quindi accessibile solo o digitandone il percorso o facendo visualizzare al sistema anche cartelle e file nascosti.



3
Codice Codice Effettare una chiamata con Skype da 4D
E' possibile, direttamente da 4th Dimension, effettuare una chiamata con Skype utilizzando molto semplicemente la sintassi:

OPEN WEB URL("Skype:Nome_Utente?Call";*)

Novita' Novita' 4D 2004: Raggruppare i radio button
Nelle precedenti versioni di 4D i radio button all'interno dei form venivano raggruppati in base alla prima lettera del nome della variabile (m_button1, m_button2, m_button3, ecc.).

Essendo questo approccio in certi casi insufficiente, dalla versione 2004 è possibile utilizzare il comando Group nel menu Object del form per raggruppare i radio button indipendentemente dal nome.

Per ragioni di compatibilità è comunque possibile continuare ad utilizzare il vecchio metodo di raggruppamento selezionando il check box Radio buttons grouped by name nella pagina "Compatibility" della voce "Application" nelle Preferences di 4th Dimension.

Info Info LAUNCH EXTERNAL PROCESS non esegue i comandi
Usando LAUNCH EXTERNAL PROCESS per eseguire comandi esterni come ad esempio l'apertura di un file attraverso un'altra applicazione, è buona regola inserire i riferimenti ai file (l'applicazione e il file da aprire) tra doppi apici, soprattutto nel caso in cui i nomi degli stessi o delle cartelle contengano degli spazi, poiché altrimenti LAUNCH EXTERNAL PROCESS non troverà i percorsi richiesti.
Un esempio potrebbe essere:

$mydoc:="C:\\Programmi\\Microsoft Office\\Office11\\WINWORD.EXE \"C:\\Documents and Settings\\Amministratore\\Documenti\\test 2.doc\""

LAUNCH EXTERNAL PROCESS($mydoc;$tIn;$tOut)


Info Info Sicurezza nelle comunicazioni client - server su WAN
Esiste la possibilità di abilitare la crittografia fra client e server 4d, ma in realtà le prestazioni calano notevolmente.

In linea generale le comunicazioni tra client e server 4d non sono facilmente intellegibili: mi spiego con un esempio specifico.

Su un classico sql, le query sono stringhe dove compaiono nomi di tabelle e campi, così come la risposta del server sql è sempre espressa in formato testo.

Su 4d invece il codice di comunicazione è tokenizzato e suddiviso in piccoli pacchetti per quanto riguarda i comandi (una query può essere spezzata anche in diverse righe di codice non consecutive) e i dati risultanti per quanto riguarda booleani, interi, reali, immagini e blob sono codificati e "affogati" nei singoli pacchetti.

Chiaramente il problema potrebbe comunque esserci in funzione dei dati che uno tratta: ad esempio se in un solo campo di testo tengo registrato il numero di carta di credito e il nome del proprietario allora questa informazione diventa intelleggibile (per quanto potrebbe essere spezzata su più pacchetti comunque).

Inoltre gli stessi pacchetti del protocollo interno 4d (non documentato pubblicamente) sono molto spezzettati rispetto alla normale comunicazione html; questo spiega il perchè la crittografia fra client e server diventa molto meno efficiente che nella normale navigazione su browser, appunto perché va a codificare-decodificare molti piccoli dati.

Le soluzioni più semplici per avere la sicurezza nella comunicazione dei dati su Wan sono:

- usare una vpn protetta fra i due router;
- dedicare, accanto al server, una macchina come terminal server (microsoft o citryx metaframe) dove aprire i client da remoto.

  La prima soluzione è molto meno costosa, la seconda è peraltro più efficiente perchè quasi indipendente dai cali di prestazione della rete (le uniche informazioni che passano sono alcuni dati sui pixel del video): l'unica controindicazione però del terminal server potrebbe essere il caso in cui si debba usare qualcosa di meno gestibile tramite questa tecnologia (per capirci, una seriale che controlla un registratore di cassa).
Info Info Ordinamento dei record selezionati con QUERY WITH ARRAY
Utilizzando il comando QUERY WITH ARRAY, i record popolano la selezione SEMPRE seguendo come ordine il record number.
Tecniche Tecniche Esempio di scambio array tra Server e Client
All'interno di 4D Client è possibile leggere il contenuto di una variabile (ad esempio un array) appartenente ad un processo residente sul server (stored procedure) utilizzando il comando GET PROCESS VARIABLE. Ecco un esempio di comunicazione:

`Method del Client
C_LONGINT(spErrCode)
ARRAY TEXT(myarr1;0)
$spProcessID:=Execute on server("MStoreProc1";128*1024;"Server 1")

Repeat
      DELAY PROCESS(Current process;300)
      GET PROCESS VARIABLE($spProcessID;spErrCode;spErrCode)
      If (Undefined(spErrCode))
           spErrCode:=1
      End if
Until (spErrCode<=0)
GET PROCESS VARIABLE($spProcessID;myarr1;myarr1)
spErrCode:=1
SET PROCESS VARIABLE($spProcessID;spErrCode;spErrCode)

`-------------------------------------------------------------------------------------------------------------------

`Method: MStoreProc1
`Description: Stored Procedure che ritorna un array.
C_LONGINT(spErrCode)
ARRAY TEXT(myarr1;0)
      ` Operazione non conclusa, imposto spErrCode a 1
spErrCode:=1
myarr1:=APPEND TO ARRAY(myarr1;"AAA")
myarr1:=APPEND TO ARRAY(myarr1;"BBB")
myarr1:=APPEND TO ARRAY(myarr1;"CCC")

spErrCode:=0
      ` Aspetto che il client prenda i risultati e me lo comunichi
Repeat
      DELAY PROCESS(Current process;1)
Until (spErrCode>0)

Codice Codice Svuotare velocemente gli array di testo su Windows
Si possono incontrare problemi di prestazioni di 4D su Windows quando si deve svuotare un array (solitamente per portarne la dimensione a 0).
Ad esempio, il seguente metodo verrà eseguito per un tempo NON ragionevole, su una macchina Windows:

`=====
C_LONGINT($size;$i;$start;$end)
$size:=100000
$start:=Milliseconds
ARRAY TEXT($text;$size)
ARRAY TEXT($text2;$size)
For ($i;1;$size)
    $text{$i}:="blabla"+String($i)
    $text2{$i}:="blabla"+String($i)
End for
ARRAY TEXT($text;0)
ARRAY TEXT($text2;0)
$end:=Milliseconds
`=====


Però, se svuotiamo il contenuto degli elementi degli array prima di portarne la dimensione a 0, il metodo diventerà VELOCISSIMO:

`=====
C_LONGINT($size;$i;$start;$end)
$size:=100000
$start:=Milliseconds
ARRAY TEXT($text;$size)
ARRAY TEXT($text2;$size)
For ($i;1;$size)
    $text{$i}:="blabla"+String($i)
    $text2{$i}:="blabla"+String($i)
End for
For ($i;1;$size)
    $text{$i}:=""
    $text2{$i}:=""
End for
ARRAY TEXT($text;0)
ARRAY TEXT($text2;0)
$end:=Milliseconds
`=====

E ciò semplicemente perché lo svuotamento è nettamente più rapito su array di elementi "vuoti".
Info Info 4D 2004 - Nuove modalità di accesso a comandi e metodi nelle formule
Il Quick Report usa il "Formula Editor" come interfaccia per metodi e comandi.
Nelle versioni precedenti era possibile utilizzare qualsiasi comando o metodo nelle formule. Con la versione 2004, con l'obiettivo di rendere più sicuri i dati, è necessario esplicitare quali sono i metodi che è consentitio inserire nelle formule, e i comandi 4D possono essere chiamati solo all'interno di metodi consentiti.

Per ammettere un metodo alla lista dei metodi utilizzabili nel formula editor si usa il comando:

SET ALLOWED METHODS($myarray)

dove myarray è un array di tipo string contenente i nomi dei metodi ammessi. E' consentito usare anche la @ come stringa joly per selezionare più metodi.

E' possibile controllare l'elenco degli array consentiti nel formula editor con GET ALLOWED METHODS(setMethodsArray). Se SET ALLOWED METHODS non è stato usato nel processo corrente, GET ALLOWED METHODS ritornerà un array di zero elementi.

I database che utilizzavano formule di vecchio tipo con le versioni precedenti dovranno essere aggiornati utilizzando i cambiamenti descritti.

Tecniche Tecniche Uso combinato della sintassi di SELECTION TO ARRAY
Il comando SELECTION TO ARRAY riceve come parametri di posto dispari (il primo, il terzo, il quinto...) o un campo o una tabella. Queste due sintassi possono essere usate anche contemporaneamente, scrivendo ad esempio:

SELECTION TO ARRAY([Clienti];alRecordNumbers;[Clienti]Nome; asNomi)

dove alRecordNumbers conterrà i record numbers dei record esportati e asNomi i nomi. Questo sistema è utile se nella tabella da cui vengono estratti i dati non esiste un campo che sia chiave primaria per la tabella, o comunque se si vuole, ad esempio, accedere in un secondo tempo al record corrispondente utilizzando GOTO RECORD.

Per le nozioni sulle chiavi vedere questa faq.
Per la sintazzi di SELECTION TO ARRAY questa faq.
Novita' Novita' 4D 2004: Deselezionare le righe di un subform
Se serve deselezionare le righe di un subform si possono utilizzare queste due possibilità:
- creare un set vuoto e utilizzare HIGHLIGHT RECORDS col set creato se il tipo di selezione del subform è "Multiple";
- usare GOTO SELECTED RECORD([Nome_Tabella_Subform];0) se il tipo di selezione del subform è "Single".

Novita' Novita' 4D 2004: Selezionare le righe di un subform
Con la versione 2004 di 4D è possibile controllare le righe da selezionare all'interno di un subform. Ciò è possibile grazie alle nuove funzionalità del comando HIGHLIGHT RECORDS: tale comando fin dalla versione 6.5, permetteva di selezionare i record di un set; adesso è in più possibile specificare la tabella da considerare e se abilitare o meno lo scroll automatico. La sintassi è dunque:

HIGHLIGHT RECORDS ({table}{; setName{; *}})

Tutto questo vale se nel suform è abilitata l'opzione di selezione è multipla: se la selezione è singola si deve usare invece GOTO SELECTED RECORD.

Info Info I tasti con azioni automatiche e i metodi relativi
Ai pulsanti dei form è possibile associare un metodo o un'azione automatica fra quelle definite da 4D.

Nel caso al pulsante vengano associati sia un metodo che un'azione automatica, verrà eseguito prima il metodo e solo dopo l'azione automatica; una possibilità comunque da tenere in considerazione in questo caso è quella di inserire il comando automatico direttamente nel metodo: ad esempio, se devo fare un Delete subrecord (nome dell'azione automatica che cancella i record selezionati in un form di output), ma prima (o anche dopo) devo eseguire del codice (ad esempio vedere se le schede sono bloccate da altri processi), si può togliere l'azione automatica dal pulsante "Cestino", e scrivere il metodo in modo che contenga, nel posto che serve, il controllo del LockedSet, l'uso dei record "highlighted" e il DELETE SELECTION.

Codice Codice Tutti i valori di un XML
Il seguente codice riempie in maniera ricorsiva l'array attribute2DArr con tutti gli attributi e i valori presenti in un file XML. Volendo fare il parse di un XML risulta molto utile. Il metodo riceve come parametro il riferimento ottenuto tramite il comando DOM Parse XML source e la dichiarazione dell'array
ARRAY TEXT(attribute2DArr;0;0)
deve essere fatta al di fuori del metodo.


C_TEXT($elementName;$elementValue)
C_TEXT($attributeName;$attributeValue)
$elementRef:=$1

    `prendi le info sull'elemento ------------------------
DOM GET XML ELEMENT NAME($elementRef;$elementName)
DOM GET XML ELEMENT VALUE($elementRef;$elementValue)
$elementID:=0

    `prendi le info sull'attributo----------------------
$numAttributes:=DOM Count XML attributes($1)

If ($numAttributes#0) `lo metto nell'array
      $sizeOfArray:=Size of array(attribute2DArr)+1  
      INSERT ELEMENT(attribute2DArr;$sizeOfArray)
      For ($i;1;$numAttributes)
           DOM GET XML ATTRIBUTE BY INDEX($1;$i;$attributeName;$attributeValue)
           attributeTxt:=$attributeName+": "+$attributeValue
           APPEND TO ARRAY(attribute2DArr{$sizeOfArray};attributeTxt)
      End for
End if

    `percorro l'albero ricorsivamente-------------------
$elementRef:=DOM Get first child XML element($elementRef)
If (OK=1)  `esiste un figlio
      While (OK=1)
           Get_xmlParseTree ($elementRef)
           $elementRef:=DOM Get Next sibling XML element($elementRef)
      End while
End if

Codice Codice Un esempio per Application type
A questo indirizzo trovate la faq relativa al comando Application type. Ecco un esempio di utilizzo.
Supponiamo di voler distinguere ottenere la data corrente dal server (se siamo in modalità client/server) o dal computer locale. il codice da eseguire sarà:

C_DATE($0)
C_DATE($currentDate)

If (Application type=4D Client )
      $currentDate:=Current date(*)
Else `se siamo sul server o in monoutenza
        $currentDate:=Current date
End if

$0:=$currentDate

Info Info Crash di 4D: come usare il log col backup su db danneggiati
Nel caso una base dati diventi inutilizzabile, col file log è possibile riportare una base dati alla situazione attuale in maniera "funzionante". Vediamo come fare.

- Intanto per sicurezza fare una copia della cartella contenente il programma (ad esempio, se il programma ha un link sul desktop/scrivania, basta fare tasto di destra sul collegamento, Proprietà, Trova destinazione / su Finder Archivio, Trova originale).
- Aprire 4D/4D Server e scegliere "Restore database"; utilizzare il backup più recente; i dati del backup verranno salvati in una cartella.
- Aprire il database in questa cartella. 4D chiederà di scegliere un file log. Scegliere il file log (.4DL) presente nella cartella originale del programma (o nella copia che avevamo fatto all'inizio).
- I dati del log verranno così integrati alla base dati. Chiudere 4D
- Sostituire i file dati (4DD e 4DR o .data) della cartella originale del programma con i dati presenti nella cartella di backup e il gioco è fatto.

Stile Stile La prima tabella della struttura
Una caratteristica che gli sviluppatori ereditano dai vecchi progetti, anche se non sarebbe più necessaria, è la presenza di una tabella "inutile" (per comodità solitamente la prima) dove viene inserito un unico record.

Questa tabella viene solitamente utilizzata per mostrare form non legati ad una tabella specifica.

Il motivo era dato dall'impossibilità di poter utilizzare le dialog per effettuare degli inserimenti. Si eseguiva dunque un ADD RECORD (seguito alla fine da un CANCEL) su quella tabella usando come form di input un form qualsiasi della tabella fittizia.
Uso il passato perché con le versioni più recenti di 4D il comando DIALOG permette l'inserimento dei dati nei form.
Codice Codice 4d e PHP: un esempio concreto
Con questo articolo voglio estendere quando gia' descritto nell'articolo di
Serena Zanfini sull'integrazione di 4D ed il linguaggio PHP (il link
dell'articolo e'
http://www.sviluppo4d.it/4DCGI/Detail_FAQ_Display?ID=190 ).

Tipicamente, in molti progetti di siti web, si utilizzano pagine dinamiche PHP per
interfacciarsi ad un database SQL, tipicamente MySQL (basti pensare ai numerosi
pacchetti di installazione di Apache+PHP+MySQL che ci sono in rete: xampp, easyphp, ...).

Tipicamente il linguaggio PHP fornisce i costrutti per interfacciarsi a questi
database, ma questo non accade per 4D. E non esiste neache una soluzione del tipo
"4D Open for PHP".

E possibile pero' accedere ai dati di 4D pubblicando alcune procedure come Web Services,
tramite SOAP (Simple Object Access Protocol, i dettagli li trovate all'indirizzo
www.w3.org/TR/soap/).

Ovviamente non e' necessario che WebServer e 4D siano installati sulla stessa
macchina, descrivero' in seguito tale dettaglio.

Innanzitutto dovete avere una macchina con Apache e PHP installati: se siete in
ambiente Mac OS X potete usare anche l'installazione fornita con il sistema altrimenti
potete utilizzare i pacchetti forniti da Server Logistics (http://www.serverlogistics.com/)
che sono semplicissimi da installare. Se invece il vostro WebServer deve
essere installato in ambiente Windows, potete usare Xampp (http://www.apachefriends.org/en/xampp.html)
o EasyPHP (http://easyphp.org).

A questo punto potete procedere alla configurazione del database 4D.

Quello che vi serve e' innanzitutto la licenza Web di 4D oppure la licenza per il
solo modulo Web Services.

Per quanto riguarda la configurazione del Web Server, vi consiglio di impostare il
numero della porta attraverso la finestra delle Preferenze. Personalmente ho impostato
tale porta al numero 5200. Mettete anche la spunta su "Publish Database as Startup", in modo
da non dover avviare il Web Server tutte le volte a mano. Per quanto riguarda i
Web Services, mettete la spunta su "Allow Web Services Requests".

Per creare un nuovo Web Service e' sufficiente creare un nuovo metodo, sia esso
wsTest. Per questo metodo vanno impostate le proprieta' "Offered as a Web Service" e
"Published in WDSL". Ora possiamo scrivere un normale metodo che accetta argomenti e
restituisce un valore, poi va' inserito del codice opportuno in modo che tali parametri
siano accettati. Sia wsTest il nodi di questo metodo.

Prima di descrivere quali modifiche fare al metodo rispetto alla forma standard,
descriviamo il codice PHP per interagire con tale metodo o Web Service.

E' necessaria un libreria PHP che si chiama NuSOAP, liberamente scaricabile
dal sito web http://dietrich.ganx4.com/nusoap/ dove e' disponibile anche la documentazione.
Il file della libreria, nusoap.php, va incluso nel codice della pagina PHP che
fa' la chiamata al server 4D. Personalmente ho copiato questo file all'indirizzo
/include del Web Server, in modo da non aver problemi con i percorsi per
l'inclusione: infatti per includere la libreria utilizzo il comando:

require_once($_SERVER['DOCUMENT_ROOT'].'/include/nusoap.php');

Inoltre, come avevo accennato in precedenza, per essere indipendenti dall'host dove
risiede il server 4D, ho definito un file INI, che ho chiamato config.ini cosi' strutturato:

[Principale]
4DServerIP = 192.168.10.5
4DServerPort = 5200
WebServerIP = 192.168.10.5

Questi parametri vengono letti tramite la funzione parse_ini_file di PHP, che trasforma
questo file in un array associativo.

Siamo ora in grado di comprendere il seguente codice PHP:

$config = parse_ini_file("config/config.ini");
// faccio la chiamata al server 4D per avere il controllo dell'username
// e della password
require_once($_SERVER['DOCUMENT_ROOT'].'/include/nusoap.php');
$sc = "http://".$config['4DServerIP'].":";
$sc.=$config['4DServerPort']."/4DSOAP";
$soapclient = new soapclient($sc);
$parameters = array('username'=>'mionome', 'password'=>'miapassword');
$ret = $soapclient->call('wsTest',$parameters);
unset($soapclient);
If (!$ret) {
print "Errore SOAP:" . $soapclient->getError() . '\n

';
exit;
} Else {
print "Funziona!";
}

Come e' evidente in questa piccola porzione di codice, e' stato definito una array
associativo i cui campi sono 'username' e 'password', la chiamata e' effettuata
tramite la call. Il fatto che il PHP sia flessibile con i tipi ci avvantaggia un po'
per quanto riguarda la variabile del risultato, l'unica cosa che dobbiamo controllare
e' che non ci sia stato un errore nella chiamata del Web Service.

Il metodo wsTest che viene eseguito e' il seguente:

    `
    `Autenticazione
    `
    `Parametri:
    `$0 TEXT- risultato
    `$1 TEXT- username
    `$2 TEXT- password
C_TEXT($0)
C_TEXT($1)
C_TEXT($2)
SOAP DECLARATION($0;Is Text ;SOAP Output )
SOAP DECLARATION($1;Is Text ;SOAP Input ;"username")
SOAP DECLARATION($2;Is Text ;SOAP Input ;"password")
ALL RECORDS([Utenti])
QUERY([Utenti];[Utenti]UserID=$1)
If (Records in selection([Utenti]) # 1)
$0:="Utente o password errata."
Else
FIRST RECORD([Utenti])
If ([Utenti]Password = $2)
$0:="Ok"
Else
$0:="Utente o password errata."
End if
End if


E' facile notare che la dichiarazione dei tipi dei parametri precede quella
della dichiarazione SOAP, in modo da poter usare questa funzione non necessariamente
attraverso una chiamata SOAP.
I tipi di dato della dichiarazione SOAP possibili sono (il testo che va' inserito nella
dichiarazione):

Is BLOB
Is Boolean
Is Integer
Is LongInt
Is Real
Boolean array
String array
Date array
Integer array
LongInt array
Real array
Text array
Is Text
Is Date
Is Time
Is String Var

L'ultimo campo della dichiarazione SOAP deve essere il nome della variabile
nell'array associativo definito in PHP. Se decidiamo di ottenere piu' di una
variabile in uscita, nel codice PHP bastera' aggiungere varie dichiarazioni di SOAP Output,
ed il risultato in PHP sara' un array associativo (che prende i nomi delle variabili
cosi' come definiti in 4D).

Un ultima nota di questo articolo va' fatta per quanto riguarda il timeout della
chiamata SOAP. Infatti, se utilizzate il debug per testare il vostro codice,
puo' darsi che durante l'esecuzione passo passo del codice 4D vada in timeout la
chiamata SOAP (tipicamente dopo 60 sencodi, ma non sono sicuro).
E' possibile pero' impostare questo tempo di timeout, in modo da provare il nostro
codice "in tranquillita'", tramite il comando 4D:

SET WEB SERVICE OPTION(Web Service HTTP Timeout;120)

Vi consiglio di inserire questo codice all'avvio dell'applicazione 4D e rimuoverlo
poi prima della compilazione.

Comandi Comandi Esportare dati con EXPORT TEXT *
Il comandi EXPORT TEXT ({table; }document) permette di esportare dati da una tabella a partire dalla selezione corrente (basta dunque eseguire precedentemente una query per esportare solo ciò che serve). L'esportazione viene eseguita utilizzando l'OUTPUT FORM e i campi vengono inseriti nell'ordine in cui si trovano nel form. Gli oggetti non campi (tipo i pulsanti) non vengono considerati.
Per ogni record esportato viene generato un evento On Load: così è possibile esportare anche variabili, impostandone i valori in questo evento.

Il nome del documento può essere quello di un documento nuovo o esistente. Se già presente, il documento viene automaticamente sovrascritto senza preavviso (se si vuole evitare ciò è bene usare prima il comando Test path name sul nome del documento da creare).

Se si passa una stringa vuota come nome del documento viene visualizzata la finestra standard di salvataggio file: se si preme Salva la variabile di sistema OK prende il valore 1.

Di default, il delimitatore di campo è la tabulazione (ASCII 9 o Tab) e quello dei record il return (ASCII 13 o CR). E' comunque possibile modificarli cambiando i valori (numerici) delle variabili FldDelimit e RecDelimit.



2
Plugin Plugin Modificare le opzioni di default di QPix
La funzione QPx_SetOption del plugin QPix di Escape permette di modificare alcune della impostazioni di default del plugin.

La sintassi è:

QPx_SetOption(optionName; numericValue; textValue)

dove

- optionName è il nome dell'opzione;
- numericValue è il nuovo valore da assegnare all'opzione (se numerico);
- textValue è il nuovo valore da assegnare all'opzione (se testuale).

Ad esempio "/pdf/gen/thumb-width" e "/pdf/gen/thumb-height" permettono di stabilire la dimensione dell'anteprima del file PDF generato da QPx_CreatePDFFile o QPx_CreatePDFBLOB; oppure "/export/exif-thumb-width" e "/export/exif-thumb-height" controllano la dimensione dell'anteprima generata da QPx_ExportImageFile, QPx_ExportImageFileToBLOB, QPx_ExportPicture, QPx_ExportPictureToBLOB, QPx_ExportAreaImage, QPx_ExportImporterImage, o QPx_ExportImporterImageToBLOB.

Plugin Plugin Creare un PDF da un'immagine
Nel plugin QPix di Escape è presente, tra le altre, una funzione che permette di ottenere un file PDF a partire da un'immagine.
La funzione è

QPx_CreatePDFFile(pdfFilePath; sourceImages; optionFlags)

dove

- pdfFilePath è il percorso al file PDF;
- sourceImages è un array di tipo testo contenente, in ogni elemento, il percorso ad uno dei file da inserire nel PDF;
- optionFlags è dato dalla combinazione dei parametri qpx_PDFShowProgress (mostra una progressi dialog, valore esadecimale 0x0001), qpx_PDFInsertAllPages (inserisce nel PDF tutte le pagine, 0x0002) e qpx_PDFCreateThumbnails (genera una miniatura della pagina della dimensione massima di 256x256 come default, ma il valore è modificabile cambiando le impostazioni del plugin con QPx_SetOption).

Codice Codice Eseguire una query con i comandi External Data Source
Ecco un esempio con i comandi necessari ad eeseguire una query via ODBC utilizzando i comandi integrati in 4D 2004.

$testoquery:="SELECT ....... FROM ....... WHERE ........"
ODBC LOGIN("DatabaseAccess";"";"")
ODBC EXECUTE($testoquery;array1;array2;......)
ODBC LOAD RECORD(ODBC All Records )
ODBC LOGOUT

Plugin Plugin Eseguire una query con 4D ODBC Pro 2004
Ecco un esempio contenente i comandi necessari per eseguire una query su un database utilizzando il plugin OBDC Pro. Si noti che arArrays è un array di puntatori agli array che dovranno ricevere i risultati e $result può gestire i vari errori che il plugin potrebbe ritornare.


C_REAL(connection_ID)
$result:=ODBC_SQLAllocConnect (connection_ID)
valuePtr:=SQL_MODE_READ_ONLY
$result:=ODBC_SQLSetConnectAttr (connection_ID;SQL_ATTR_ACCESS_MODE ;->valuePtr)
$result:=ODBC_SQLConnect (connection_ID;"DatabaseAccess";"";"")
$testoquery:="SELECT...... FROM ...... WHERE ......"
$result:=ODBC_SQLAllocStmt (connection_ID;$statementID)
$result:=ODBC_SQLPrepare ($statementID;$testoquery)
$result:=ODBC_SQLExecute ($statementID)
For ($i;1;Size of array(arArrays))
      $result:=(ODBC_SQLBindCol ($statementID;$i;arArrays{$i}))
End for
While ($result#SQL_NO_DATA)
      $result:=ODBC_SQLFetch ($statementID)
End while
$result:=0
$result:=(ODBC_SQLFreeStmt ($statementID;SQL_CLOSE ))
$result:=(ODBC_SQLFreeStmt ($statementID;SQL_UNBIND ))
$result:=(ODBC_SQLFreeStmt ($statementID;SQL_RESET_PARAMS ))
$result:=ODBC_SQLDisconnect (connection_ID)

Info Info 4D 2004: posizione dei plugin
Le cartelle Win4dx e Mac4dx nella versione 2004 continuano ad essere utilizzate per compatibilità, ma con la nuova versione la cartella che contiene i plugin è diventata unica e si chiama, guarda caso, "PlugIns".

Questa cartella può essere posizionata:
- al livello del file struttura: in questo caso i plugin installati saranno disponibili solo per quel database
- al livello dell'applicazione: in tal caso i plugin installati saranno disponibili per tutte le strutture.

Info Info Conoscere il nome di un 4D Server usando TELNET
Conoscendo IP del server e la porta di esecuzione, è possibile usare Telnet per conoscere il nome del database 4D in esecuzione.

Ricevendo un telnet, infatti, 4D Server risponde con il nome del database in esecuzione.

Plugin Plugin Il primo grafico con 4D Chart
Creare un grafico a partire da alcuni dati è davvero agevole. Basta prendere la selezione desiderata, un campo per la "x" vettoriale, un campo per la y, un campo per i valori e utilizzare il comando CT Chart data. Vediamo un esempio:

ALL RECORDS([Table1])
tableNum:=Table(->[Table1])
categoryField:=Field(->[Table1]Field1)
seriesField:=Field(->[Table1]Field2)
valuesField:=Field(->[Table1]Field3)

vChart:=Open external window(50;50;650;600;8;"Grafico Profitti";"_4D Chart")
CT SET DOCUMENT SIZE (vChart;586;766)
$err:=CT Chart data (vChart;2;1;0;0;tableNum;categoryField;seriesField;valuesField)


Il comando CT SET DOCUMENT SIZE è necessario per fare sì che, in caso di stampa del documento, il grafico venga stampato su una sola pagina.
Per i comandi di stampa con 4D Chart vi rimandiamo a questa faq.

Info Info Interrompere una ricerca
La ricerca "Find in database" permette di effettuare ricerche di stringhe anche in tutto il database (Form, Tabelle, methods, ecc.).

Su grandi database o con connessioni di rete lente (tipo wi-fi) la ricerca può essere assai lunga e durare più di quanto previsto.

Se ciò succede, la ricerca può essere interrotta molto semplicemente premendo il tasto ESC.

Tecniche Tecniche Importare dati da Excel a 4D
I dati presenti in un foglio di calcolo di Excel possono essere importati in 4th Dimension in vari modi.
Il più immediato è quello esportare i dati in uno dei formati che 4D può importare in maniera automatica: i file CSV, TXT, SYLK, e DIFF.

I dati, ad esempio, possono essere salvati in formato testo delimitato da tabulazione. Per fare ciò, scegliere "File" "Salva con nome..." e scegliere come tipo di file "Testo delimitato da tabulazione", e quindi eseguire l'esportazione.
Per importare i dati, in modalità "User" scegliere "File / Import / From File...".

Altre possibilità per eseguire l'importazione sono aprire uno stream DDE oppure via ODBC.

Codice Codice Trovare errori nei TAG html [2]
` ======================================
    ` CHECK HTML TAGS di Roberto Vergani (05-2006)
    ` ======================================

C_TEXT($info;$sourceText;$resultText;$tag)
C_INTEGER($X;$J;$k;$n;$size;$vLineNumber;$startLine;$offset)
C_STRING(2;$char)
C_BOOLEAN($notFound)

ARRAY TEXT($vtErrorText;0)

$size:=Test clipboard("TEXT")

Case of
  : ($size<=0)
    ALERT("Gli appunti non contengono testo, elaborazione interrotta.")
  : ($size>31000)
    ALERT("Il contenuto degli appunti supera i 31K, elaborazione interrotta.")
  Else
    $info:="Questa procedura controlla la parità dei tags HTML"+Char(13)
    $info:=$info+"Procedi dopo avere copiato in appunti il codice sorgente."
    CONFIRM($info;"Procedi";"Annulla")
    
    If (OK=1)
      
          ` ECCEZIONI: questi TAG usualmente non hanno chiusura, SONO IGNORATI
          ` aggiungi o togli quello che vuoi, inserisci senza i delimitatori < >
          ` tutti i tags che iniziano con       
      ARRAY TEXT($vtExceptions;13)
      $vtExceptions{1}:="img"
      $vtExceptions{2}:="br"
      $vtExceptions{3}:="br/"
      $vtExceptions{4}:="input"
      $vtExceptions{5}:="link"
      $vtExceptions{6}:="meta"
      $vtExceptions{7}:="?xml"
      $vtExceptions{8}:="area"
      $vtExceptions{9}:="param"
      $vtExceptions{10}:="base"
      $vtExceptions{11}:="hr"
      $vtExceptions{12}:="frame"
      $vtExceptions{13}:="spacer"
          
      
      ARRAY TEXT($vtRighe;0)
      $SourceText:=Get text from clipboard
      
      If (OK=0)
        ALERT("Si è verificato un errore nel leggere gli appunti (procedura interrotta).")
      Else
        SET CURSOR(4)
        
        ARRAY TEXT($vtTags;0)
        ARRAY INTEGER($vtCount;0)
        ARRAY INTEGER($vtLine;0;0)
        ARRAY INTEGER($vtPosition;0;0)
        $vLineNumber:=1
        $startLine:=1
        $X:=1
        
        If ($size>3000)
          Open window((Screen width-300)/2;(Screen height-150)/2;((Screen width-300)/2)+300;((Screen height-150)/2)+150;1)
        End if
        
        Repeat
          
          $char:=($SourceText?$X?)
          GOTO XY(10;6)
          MESSAGE("Parsing: "+String($X)+" / "+String($size)+" bytes")
          
          Case of
          
           : ($char=Char(13))
           $vLineNumber:=$vLineNumber+1
           $X:=$X+1
           $startLine:=$X
          
           : ($char="<") & (($SourceText?$X+Num($X<($size+1))?)#"!") ` ignora tutti i tag che iniziano con           
           $offset:=1
           Repeat
           $offset:=$offset+1
           Until (($SourceText?$X+$offset?=">") | ($SourceText?$X+$offset?=Char(32)) | ($SourceText?$X+$offset?=Char(13)))
           $tag:=Substring($SourceText;$X+1;$offset-1)
          
           $n:=Find in array($vtExceptions;$tag)
           If ($n=-1) ` se il tag non è un'eccezione da ignorare
           If (Ascii($tag)=47) ` se il tag inizia con ascii 47 = /
           $notFound:=False
           $tag:=Substring($tag;2)
           $n:=Find in array($vtTags;$tag)
           If ($n=-1) ` è chiuso un tag che compare per la prima volta e non è presente nell'elenco dei tag esistenti
           $notFound:=True
           Else
           If (Size of array($vtLine{$n})=0)
           $notFound:=True
           Else
           DELETE ELEMENT($vtLine{$n};Size of array($vtLine{$n}))
           DELETE ELEMENT($vtPosition{$n};Size of array($vtPosition{$n}))
           End if
           End if
           If ($notFound=True)
           INSERT ELEMENT($vtErrorText;Size of array($vtErrorText)+1)
           $vtErrorText{Size of array($vtErrorText)}:=" tag chiuso senza apertura, linea "+String($vLineNumber)+" posizione "+String($X-$startLine+1)
           End if
           Else
           $n:=Find in array($vtTags;$tag)
           If ($n=-1)
           $n:=Size of array($vtTags)+1
           INSERT ELEMENT($vtTags;$n)
           INSERT ELEMENT($vtCount;$n)
           INSERT ELEMENT($vtLine;$n)
           INSERT ELEMENT($vtPosition;$n)
           End if
           $J:=Size of array($vtLine{$n})+1
           INSERT ELEMENT($vtLine{$n};$J)
           INSERT ELEMENT($vtPosition{$n};$J)
           $vtTags{$n}:=$tag
           $vtCount{$n}:=$vtCount{$n}+1
           $vtLine{$n}{$J}:=$vLineNumber
           $vtPosition{$n}{$J}:=$X-$startLine+1
           End if
          
           End if
           $X:=$X+$offset-1
           Else
           $X:=$X+1
          End case
        Until ($X>Length($SourceText))
        
        If ($size>8000)
          CLOSE WINDOW
        End if
        
        $resultText:="CHECK HTML TAGS di Roberto Vergani (05-2006)"+(Char(13)*3)
        $n:=Size of array($vtErrorText)
        $k:=0
        For ($X;1;Size of array($vtLine))
          $k:=$k+Size of array($vtLine{$X})
        End for
        If ($n>0) | ($k>0)
          $resultText:=$resultText+"### SONO STATI RILEVATI ERRORI :"+(Char(13)*2)
          For ($X;1;Size of array($vtLine))
           If (Size of array($vtLine{$X})>0)
           For ($J;1;Size of array($vtLine{$X}))
           $resultText:=$resultText+"<"+$vtTags{$X}+"> tag aperto e non chiuso, linea "+String($vtLine{$X}{$J})+" posizione "+String($vtPosition{$X}{$J})+Char(13)
           End for
           End if
          End for
          $resultText:=$resultText+Char(13)
          For ($X;1;$n)
           $resultText:=$resultText+$vtErrorText{$X}+Char(13)
          End for
          $info:="### RILEVATI ERRORI."+Char(13)
        Else
          $resultText:=$resultText+"Il codice esaminato sembra essere OK."
          $info:="Nessun errore."+Char(13)
        End if
        $resultText:=$resultText+(Char(13)*3)
        $resultText:=$resultText+"Sono stati esaminati:"+(Char(13)*2)
        For ($X;1;Size of array($vtTags))
          $value:=String($vtCount{$X}) ` riutilizzo di variabile
          $resultText:=$resultText+(" "*(5-Length($value)))+$value+" tag <"+$vtTags{$X}+">"+Char(13)
        End for
        SET TEXT TO CLIPBOARD($resultText)
        SET CURSOR(0)
        ALERT($info+"L'esito dell'elaborazione è contenuto negli appunti, incolla dove vuoi.")
      End if
    End if
End case


Codice Codice Trovare errori nei TAG html [1]
Chi costruisce pagine html da utilizzare in modalità non contestuale utilizzando dati e variabili in modo dinamico, in genere deve costruire blocchi di codice html che costituiscono la pagina con i dati, per esempio un blocco di intestazione, un blocco contenuto in un loop e ripetuto per ogni record, eventuali break, un blocco di fine pagina.
E’ praticamente sempre necessario costruire tabelle ed avviene spesso di commettere errori nella parità dei TAG, dove per parità intendo che ad ogni TAG di apertura corrisponda il relativo TAG di chiusura: .

Recentemente mi è avvenuto di lavorare su pagine molto complesse e, spostando blocchi di codice dentro e fuori dai loop, ad un certo punto avevo errori di visualizzazione o negli allineamenti e non riuscivo più a controllare se tutti i TAG fossero a posto. Ho cercato ma non ho trovato un applicativo semplice che controllasse il codice. Allora l’ho scritto.

E’ un method che non usa form e che si può inserire in qualunque struttura. Esamina il codice presente negli appunti ed ha quindi il limite di 32K di lunghezza del sorgente ma è più che sufficiente per l’esigenza di controllare codice in 4D.

L’uso è immediato: copiare il codice da esaminare e lanciare il method dall’ambiente user, a fine elaborazione viene prodotto un report che è collocato sempre negli appunti: incollare dove possa essere letto ed eventualmente stampato. Naturalmente questo method considera tutte le stringhe che iniziano con il carattere “<“ e il report potrebbe riportare presunti errori che invece non sono tali: basta ignorarli.

Questo method non è un semplice contatore che somma aperture e chiusure ma lavora rispettando la nidifcazione dei TAG.
Il report elenca gli errori riportando i riferimenti di riga e posizione in cui sono stati trovati. Prevede anche delle eccezioni, TAG che non hanno chiusura e che vengono ignorati; l’elenco delle eccezioni è facilmente modificabile. Può essere usato anche quando il codice html è contenuto in istruzioni 4D, esempio:



addToBody ("<table border=0 cellpadding=0 cellspacing=0 bgcolor="+Char(34)+myVar+Char(34)+">")
addToBody ("<tr>")
addToBody ("<td height=12 width=8 valign=top><img src="+Char(34)+""+Char(34)+" border=0 width=1 height=1 alt="+Char(34)+""+Char(34)+"></td>")
addToBody ("<td width=582 valign=top>")
addToBody ("<p><font size=-1><span class="+Char(34)+"style108"+Char(34)+"><font color="+ myVar+"</font></span></font></p></td>")
addToBody ("<td width=10 valign=top><img src="+Char(34)+""+Char(34)+" border=0 width=1 height=1 alt="+Char(34)+""+Char(34)+"></td>")
addToBody ("</tr>")
addToBody ("</table>")

Nel caso di codice 4D non ricompone i tag eventualmente tagliati dalle virgolette per il limite della lunghezza delle stringhe.

Mi sono divertito ad esaminare parecchie pagine scaricate qua e la per la rete ed è curioso notare quante contengano errori, errori che i browser attuali sono abbastanza intelligenti da perdonare e che quindi risultano non visibili.

Codice Codice Messenger, AIM o ICQ fra client 4D con poco codice
In modalità client/server è possibile realizzare un sistema di messaggistica istantanea (tipo Messenger, AIM, ICQ) utilizzando pochissime righe di codice. Vediamo come.

Il seguente metodo, Registration, permette di registrare il client in modo da renderlo pronto a ricevere messaggi da altri 4D Client.


`--------------------
UNREGISTER CLIENT
Repeat
      vPseudoName:=Request("Enter your name:";"User";"OK";"Cancel")
Until ((OK=0) | (vPseudoName # ""))
If (OK=0)
      ...` Non fa niente
Else
      REGISTER CLIENT(vPseudoName)
End if
`--------------------



L'istruzione che segue mette in moto il processo che consente di ottenere la lista aggiornata dei client collegati. Una buona idea potrebbe essere quella di inserirla nel On Startup Database Method:

PrClientList:=New process("4D Client List";64000;"Lista dei registered clients")


Il metodo 4D Client List permette di ottenere la lista dei client registrati:


`--------------------
If (Application type=4D Client)
` il codice che segue è valido solo in modalità client/server
      $Ref:=Open window(100;100;300;400;-(Palette window+Has window title);"Lista dei client registrati")
      Repeat
           GET REGISTERED CLIENTS($ClientList;$ListeCharge)
           `Lista dei client in $ClientList
           ERASE WINDOW($Ref)
           GOTO XY(0;0)
           For ($p;1;Size of array($ClientList))
           MESSAGE($ClientList{$p}+Char(Carriage return))
           End for
           `lo mostra qualche secondo
           DELAY PROCESS(Current process;60)
      Until (False) ` loop infinito
End if
`--------------------


Il seguente metodo manda un messaggio a un altro 4D Client usando il metodo Display_Message


`--------------------
$Addressee:=Request("Destinatario del messaggio:";"")
` Inserire uno dei nomi visualizzati dalla finestra aperta in
` On Startup database method
If (OK # 0)
      $Message:=Request("Messaggio:") ` il messaggio
      If (OK # 0)
           EXECUTE ON CLIENT($Addressee;"Display_Message";$Message) ` manda il messaggio
      End if
End if
`--------------------



Ecco il metodo Display_Message

`--------------------

C_TEXT($1)
ALERT($1)

`--------------------



Infine, il metodo che permetta ad un client di non essere più visibile fra i client collegati in modo da non poter ricevere messaggi dovrà contenere la sola istruzione:

UNREGISTER CLIENT


Info Info Differenze fra SET PROCESS VARIABLE e VARIABLE TO VARIABLE
4D fornisce due comandi per far impostare il valore di una variabile in un processo diverso: SET PROCESS VARIABLE e VARIABLE TO VARIABLE. I due comandi, benché simili, presentano due sostanziali differenze:
- SET PROCESS VARIABLE permette di passare alla variabile di destinazione anche una espressione, invece di una variabile.
- SET PROCESS VARIABLE non permette di far passare fra i processi un array intero, ma solo elementi di array. Per far passare fra processi degli array bisogna usare VARIABLE TO VARIABLE (tranne array di puntatori e array bidimensionali, che non possono essere passati).

Entrambi i comandi non accettano inoltre:
- variabili locali come destinazione;
- elementi di array di puntatori;
- elementi di array bidimensionali.

Codice Codice Liberare la memoria dopo aver usato un XML
In linea generale, tutte le volte che si usa un XML bisogna pulirlo alla fine, perchè l'occupazione in memoria non è della sola variabile longint Riferimento, ma di una struttura ben più grande a cui questa punta.

Quindi, la sequenza è:

1) Riferimento:=DOM Create XML Ref("XYZ")`qui lo crei

2..n) ... qui costruisci l'xml

n+1) DOM EXPORT TO VAR(Riferimento;blob) `qui lo usi, esporti, etc

n+2) DOM CLOSE XML (Riferimento) `<-----AGGIUNGI QUESTO !!!!
Tecniche Tecniche Deselezionare gli elementi di una listbox **
Le list box "ricordano" la riga selezionata, il che significa che visualizzando uan listbox già visualizzata potremmo trovare una riga già selezionata.
Per evitare ciò, se arrEsempio è uno degli array usati dalla listbox ListBox, basta scrivere:

SELECT LISTBOX ROW (ListBox; Size of array(arrEsempio)+1)



7
Plugin Plugin Una procedura per ordinare con 4D Open for Java
4D Open for Java è un insieme di comandi che permette di interrogare un 4D Server via Java: Linux, Unix, Windows, Mac, ecc. sono tutti sistemi che possono dunque interagire con un server 4D.

Ecco, ad esempio, una procedura per effettuare un ordinamento:

opSelection selection = process.AllRecords(table);
selection.mTableNumber = 1;
opFieldArray fieldArray = new opFieldArray(1);
fieldArray.mTargetTable = 1;

fieldArray.mFieldArray[0] = new opField(1,1);
fieldArray.mFieldArray[0].mFieldNumber=1;
fieldArray.mFieldArray[0].mOrdering=GREATER_THAN; // (*)Ascending order
fieldArray.mFieldArray[0].mFieldType=1;

process.OrderBy(fieldArray);

process.RecordsInSelection(selection);
int found = selection.mRecordsInSelection;

opDataArray dataArray[] = new opDataArray[1];
dataArray[0] = new opDataArray(found);
process.SelectionToArray(dataArray,fieldArray);

for(short i=0;i{
System.out.println(dataArray[0].mDataArray[i].mString);
}


Codice Codice Calcolo del giorno di Pasqua
Da un recente "daily tip" di 4DToday, segnaliamo il codice che permette di ottenere la data del giorno di Pasqua per un certo anno, che è il parametro da passare al metodo:


C_INTEGER($1;$G;$I;$C;$H;$I;$J;$L;$M;$D)
C_STRING(6;$0)

$G:=(($1%19)+1)-1
$I:=((19*$G)+15)%30
$C:=($1\100)

$H:=($C-($C\4)-(((8*$C)+13)\25)+(19*$G)+15)%30
$I:=$H-(($H\28)*(1-((29\($H+1))*((21-$G)\11))))
$J:=($1+($1\4)+$I+2-$C+($C\4))%7
$L:=$I-$J
$M:=3+(($L+40)\44)
$D:=$L+28-(31*($M\4))

$0:=String($D;"00")+"/"+String($M;"00")+"/"+String($1)





Fonte: 4DToday
Inviato da Paul Mohammadi

Info Info Tutte le faq su 4D, Access e FileMaker Pro
Tecniche Tecniche Usare 4D da remoto: alcune alternative
La possibilità di avere connessioni internet sempre attive a costi bassissimi e la necessità delle aziende di "dislocarsi" sul territorio mantenendo un unico software centrale per tutte le sedi porta spesso alla problematica: come uso 4D attraverso internet? Vediamo alcune possibilità.

Una soluzione potrebbe essere utilizzare il software attraverso interfaccia web: questo richiede l'acquisto della licenza web e consente di utilizzare il software con qualsiasi sistema operativo.

Se il server ha un indirizzo ip pubblico e' possibile usare direttamente 4D Client per connettersi a 4D Server. La "controindicazione" in tal caso è la quantità di traffico generata, che potrebbe essere notevole. Per "minimizzare" il traffico si potrebbe optare per dei servizi di tipo "terminal": l'onere del lancio del client viene demandato al server terminal, e il traffico internet generato diventa solo quello necessario alla visualizzazione dello schermo.

Plugin Plugin Cambiare il colore di un grafico: CT SET CHART FILL ATTRIBUTES
Quando si genera un grafico con 4D Chart, il plugin si occupa autonomamente di assegnare i colori alle varie serie.

Se si desidera personalizzare il colore delle serie, si utilizza il comando CT SET CHART FILL ATTRIBUTES (area; object; partType; partSpecifics; pattern; color).

Il comando prende come parametri l'ID dell'area, l'ID dell'oggetto, il tipo di oggetto, la parte specifica dell'oggetto (questi ultimi due parametri sono espressi da costanti specifiche di 4D Chart), il pattern (da 1 a 36, -1 per non variarlo) e il colore (anche qui -1 per non cambiarlo).

Ad esempio, per far diventare verde "solido" il colore della prima serie del grafico $Chart nell'area Area, scriveremo:

$Color:=CT Index to color (10)
CT SET CHART FILL ATTRIBUTES (Area;$Chart;8;100;3;$Color)

Tecniche Tecniche Metodo di indirizzamento delle stampe [1]

Sottotitolo: [Bug] [Risolto] 2003.6 e 2003.7 OSX 10.3 e 10.4 non imposta la stampante richiesta, non preleva correttamente la carta

Ho finalmente avuto il tempo di affrontare questo argomento che nel mio ambiente di lavoro mi creava problemi da quando sono passato alla v 2003.
Qui da noi ogni giorno 10 client stampano parecchio materiale, saltando dalle etichette adesive da prelevare dai vassoi, al fogli standard nei cassetti, ai cartellini a colori per il self-service, alle etichette sempre a colori dei prodotti, utilizzando 3 stampati di rete postscript in TCP/IP, alcune stampanti inkjet USB e una Inkjet A3 condivisa. Tutti gli utenti erano costretti a impostare manualmente tutti i parametri per ogni stampa eseguita.

Ho esaminato le informazioni che mi è stato possibile trovare in ambito internazionale, sia note tecniche sia messaggi postati ai diversi NUG, testando le soluzioni proposte (alcune delle quali abbastanza astruse, come quella di creare diverse copie dei documenti PPD e di rinominarli al volo da method per costringere l’OS ad usare la configurazione voluta...).

Ho trovato una soluzione che finalmente funziona bene e che utilizza i comandi standard, soluzione basata sulle funzioni di salvataggio e recupero dei parametri di stampa in BLOB fornite dal plug-in ACI_Pack. Ciò implica il salvataggio del setup in una table ma a mio parere ne vale la pena. La soluzione è testata con tutte le funzioni di stampa, compresa PRINT FORM, ma non con i QuickReport che non uso.

Creazione e salvataggio impostazioni di stampa, è importante la corretta successione dei comandi:

1) eventuale impostazione del formato con PAGE SETUP, se necessario;
2) ottenimento di tutti i parametri di stampa con PRINT SETTINGS;
      istruendo l’utente a impostare tutti i parametri con attenzione, anche il numero delle copie
3) trasferimento del setup in una variabile BLOB con AP Print settings to BLOB;
4) salvataggio del nome della stampante selezionata;
5) salvataggio del BLOB con i parametri.

Esempio, assumendo di avere creato una table [ParametersTable] dove registrare i parametri di stampa

C_BLOB($mioBlob)
PAGE SETUP ([myTable];"PageSetupForm")
PRINT SETTINGS
If (OK=1)
    $error:=AP Print settings to BLOB ($mioBlob)
    If ($error#1)
           ALERT ("Errore generando il BLOB dei Parametri di stampa.")
    Else
           CREATE RECORD ([ParametersTable])
           [ParametersTable]PrinterName:=Get current printer
           [ParametersTable]ParametersBLOB:=$mioBlob
           SAVE RECORD ([ParametersTable])
    End if
End if

Nota: è indispensabile trasferire i parametri in una variabile BLOB e poi assegnarla al campo BLOB, non usare la funzione AP Print settings to BLOB con un campo quale argomento della funzione perché poi il BLOB può generare errori.



1
File File Cambia stato Visibile/Invisibile di un documento
Questa struttura è un esercizio di uso della ricorsione e di come utilizzare un solo Record ma con un gran numero di subrecord, per poter utilizzare il Runtime License Light, cioè l'engine Demo free, per creare delle utility.
In realtà non sempre funziona l'attribuzione del flag visibile/invisibile (ad esempio per i pacchetti), ma chiunque voglia può proporre modifiche e varianti.

Puoi scaricarlo da qui: Invisible/Visible

File File Rinomina Documenti
Questa struttura (compilandola con il Runtime License Light, cioè l'engine Demo free) permette di scorrere tutti i Files di una Cartella rinominando gli stessi (comodo, ad esempio, per rinominare foto scaricate da una fotocamera)

Puoi scaricarlo da qui: Rinomina
Codice Codice Percorso dell'applicazione o della struttura
Questo metodo ritorna il percorso corrente dell'applicazione compilata con l'engine o della struttura sia su Mac che su PC

` Method: dammiPercorso
C_LONGINT($lun;$pos)
C_TEXT($0;$sep;$percorso)
$sep:=system folder≤length(system folder)≥
If ((Application type=4D Runtime Volume License) & ($sep=":"))
      $percorso:=Replace string(Application file;".app";"")
Else
      $percorso:=Structure file
End if
$lun:=Length($percorso)
$pos:=$lun
Repeat
      $pos:=$pos-1
Until ($percorso?$pos?=$sep)
$0:=Substring($percorso;1;$pos)
Info Info Lista dei server recenti
Quando ti colleghi ad un server con il 4D Client viene registrato un documento con il percorso nella cartella Favorites 2004.

Questa cartella si trova qui:

Windows = C:\\Documents and Settings\CurrentUser\Application Data\4D folder

Macintosh = Mac HD:Users:CurrentUser:Library:Application Support:4D
dove, il nome del disco di avvio può essere diverso da Mac HD, e al posto di CurrentUser ci sarà il nome dell'utente

Al prossimo collegamento a 4D Server, il 4D Client mostrerà prima l'elenco dei server il cui nome legge dai documenti nella cartella Favorites 2004.
Questi documenti contengono inoltre anche l'indirizzo ip del server.
Info Info A cosa serve lo stack
Usando i comandi New process o Execute on server viene utilizzato un valore che rappresenta, in byte, la dimensione dello stack. Lo stack non è la memoria totale del processo. I processi condividono la memoria per quanto riguarda record, variabili interprocesso, ecc. Un processo usa della memoria aggiuntiva per memorizzare le variabili del processo.

Lo stack contiene il resto: la "pila" delle chiamate ricorsive, con il numero e la dimensione dei parametri e delle variabili locali, i form aperti dal processo prima della sua chiusura.

Web Web Aprire un documento XML
Per aprire un documento XML da processare il comando da utilizzare è
Parse XML source (per la 2004 DOM Parse XML source).

La sintassi è:

Parse XML source (document{; validation{; dtd}})

dove document è il percorso al file, validation è un booleano che controlla se il documento rispetta o no le specifiche DTD (Document Type Declaration, una serie di regole che un documento XML deve rispettare) e il percorso dove è memorizzato il file di validazione.

Stesso principio usano i comandi Parse XML variable (DOM Parse XML variable per la 2004), ma su variabili testo o blob.

Il comando resistuisce una stringa da utilizzare come riferimento al documento.

Codice Codice Mantenere i colori del method editor
Uno dei crucci maggiori per i programmatori è quello di mantenere il più standard possibile il proprio ambiente di lavoro. Ad esempio poter mantenere gli stessi colori per il method editor tra varie macchine.

Per rendere possibile tutto questo basta riutilizzare il file 4D Preferences 2004(.RSR) che contiene tutte le informazioni di personalizzazione dell'applicazione 4th Dimension.
Questo principio è molti utile, ad esempio, per avere tutti i client con i colori "configurati" allo stesso modo.

Le cartelle dove rintracciare il file (per 4th Dimension, 4D Server e 4D Client) si possono ottenere da questa faq.

Tecniche Tecniche Metodo di indirizzamento delle stampe [3]
Applicazione

Nell’applicativo sopra citato ho creato una gestione delle stampe che salva i parametri per ogni diverso modulo funzionale (fatture, etichette, ecc. ecc.) e i parametri sono salvati separatamente per ogni utente o postazione di lavoro, perché non tutti i client accedono alle stesse stampanti. Una volta impostata una specifica stampa per una determinata procedura di una determinata postazione di lavoro, i parametri di stampa sono automaticamente proposti come default per le successive stampe identiche; è comunque sempre possibile scegliere una diversa impostazione tra quelle salvate, tutte accessibili in qualunque funzione di stampa dell’intera struttura.

Ecco un esempio dell’interfaccia (ancora stile OS 9...) in cui è fatto uso anche della possibilità di registrare il numero di copie da stampare



Dopo avere impostato i parametri per la prima volta, basta un tasto per ottenere stampe corrette; in alternativa basta scegliere una voce dal popup per cambiare la destinazione della stampa o il prelevamento della carta, selezionando una pre-impostazione salvata.

RV marzo 2006
Tecniche Tecniche Metodo di indirizzamento delle stampe [2]
Recupero e utilizzo delle impostazioni di stampa, è importante la corretta successione dei comandi:

1) impostare la stampante da usare utilizzando il nome salvato;
2) applicare i parametri di stampa con AP BLOB to print settings usando il paramType “0” per impostare sia Layout sia Print;
3) lanciare la stampa senza cambiare più nulla: no PAGE SETUP no PRINT SETTINGS altrimenti si ricade nell’indefinito.

Esempio

SET CURRENT PRINTER([ParametersTable]PrinterName)
$error:=AP BLOB to print settings ([ParametersTable]ParametersBLOB;0)
Case of
        : ($error=-1)
           ALERT ("Il BLOB dei Parametri di Stampa contiene dati errati.")
        : ($error=0)
           ALERT ("Errore di stampante non definita.")
        Else
           ` lanciare la stampa
End case

Nota bene:

- dove si fa riferimento al nome della stampante, è sempre inteso il system name e non l’user name, ovvero il nome della coda di stampa, quello che si ottiene con il comando PRINTERS LIST;

- nel caso di stampe che utilizzano la funzione Print Form, è indispensabile inizializzare il job con un PAGE BREAK(>), nota il “>”;

- questa soluzione salva tutti i parametri impostati con il PRINT SETTINGS, anche il numero di copie.

- dopo caricati i parametri, se si chiama PRINT SETTINGS si perdono le impostazioni caricate e verranno usate quelle mostrate nei dialoghi di definizione di stampa standard dell’OS;

- dopo avere caricato i parametri di stampa, se desidera esaminarli (con PRINT SETTINGS)si troverà che il secondo dialog di stampa standard dell’OS (il primo definisce la pagina, il secondo i parametri) mostrerà una Preimpostazione (Preset) che può non corrispondere a quella salvata (è definita dall’OS sarà l’ultima usata o quella di default dipende dalle impostazioni di sistema): è ininfluente, viene ignorata e verrà usata quella effettivamente salvata (salvo avere confermato i dialoghi di stampa aperti con PRINT SETTINGS).

Tecniche Tecniche Eseguire più 4DServer sulla stessa macchina *
È possibile far girare più di una applicazione 4DServer sulla stessa macchina. L'unico accorgimento che bisogna prendere è quello di "customizzare" ognuna delle sessioni.
4DServer pubblica i database sempre sulla stessa porta TCP/IP, e risulta dunque necessario modificare questo parametro per ognuno dei server da attivare.
La procedura da seguire inizia con l'individuazione del file tcp.opt nel cartella di sistema 4D; copiare questo file in ognuna delle cartelle dei 4DServer che vogliamo attivare; aprire ognuno dei file (al limite tranne uno, il 19813 standard) col Customizer e modificare il valore della porta con un numero vicino a quello visualizzato e comunque diverso da ognuno degli altri numeri assegnati.
A questo punto, lanciando 4DClient, basterà inserire come indirizzo del server xxx.zzz.yyy.aaa virgola numero della porta assegnata in precedenza.

2
Novita' Novita' 4D 2004.3 - Il comando SET SCROLLBAR VISIBLE
Nelle prime versioni della 2004, era presente il comando SHOW LISTBOX SCROLLBAR che permetteva di visualizzare le barre di scorrimento per le listbox.

Dalla versione 2004.3 questo comando è stato rimpiazzato dal più potente SET SCROLLBAR VISIBLE, che dal capitolo List Box" è stato spostato in quello "Object Properties"; questo comando può adesso essere utilizzato, oltre che sulle List box, anche su Scrollable area e Subform.

Comandi Comandi Il comando ALERT
Il comando ALERT permette di mostrare a video una dialog con un'icona, un testo da massimo 255 caratteri e un pulsante.
La sintassi è

ALERT (message{; ok button title})

dove message è il messaggio da mostrare a video (può essere un testo direttamente oppure una variabile) e, opzionale, ok button title è il testo da assegnare al pulsante OK.

Se il testo passato è superiore a 255 caratteri viene troncato.
La dimensione del pulsante viene adattata automaticamente alla dimensione del testo del pulsante.

ATTENZIONE: Il comando non deve essere eseguito all'interno dei form event On Activate oppure On Deactivate perché ciò genera un loop infinito.

Info Info Not enough stack space to complete the current method
Passando database, possibilmente sviluppati inizialmente con versioni (e computer) assai datate di 4D, alla versione 2004.3 di 4th Dimension, potrebbe d'un tratto diventare assai frequente l'errore "Not enough stack space to complete the current method": cioè, nel creare il nuovo processo con New process o Execute on server, non gli si è dato abbastanza spazio nello stack.

Il problema è che nelle ultime versioni era consigliato aumentare lo stack dai vecchissimi 32K almeno a 64K o meglio 128K. In realtà le versioni precedenti alla 2004.3 aumentavano arbitrariamente la dimensione dello stack per evitare errori al programmatore dopo una conversione di struttura: questo però ha portato ad un minor controllo di quello che succede come occupazione della memoria.
Ora 4D obbedisce strettamente alle impostazioni fatte e quindi se si ha qualche chiamata con meno di 64K di stack è facile vedere questo mesaggio.
Comandi Comandi Creare un logical mirror [1]: come funziona
Solitamente le tecniche di backup standard permettono di effettuare salvataggi (solitamente notturni o a richiesta) dei database. Però in qualche caso non basta avere una copia di backup, ma sarebbe utile fermare le operazioni di accesso al database per il minor tempo possibile.

Per venire incontro a questo tipo di esigenza, con la versione 2004.3 è comparso un nuovo tipo di backup: logical mirror.
Su macchine 4D Server diventa infatti possibile avere due programmi sempre aggiornati e funzionanti: vediamo come funziona.

Supponiamo di avere due Server, uno MacchinaPrincipale e l'altro MacchinaMirror. Su MacchinaPrincipale facciamo partire l'applicazione, eseguiamo un backup e definiamo un file log (che per MioDatabase sarà MioDatabase.4DL).

Usciamo dall'applicazione.

Copiamo tutti i file (compreso il log file) su MacchinaMirror.

Facciamo partire l'applicazione su entrambe le macchine: MacchinaMirror ci chiederà quale log file utilizzare e noi sceglieremo il file MioDatabase.4DL che abbiamo trasferito.

Decidiamo di eseguire l'aggiornamento del mirror in automatico (ad esempio dopo una certa quantità di operazioni). Eseguiamo un metodo contenente il comando New log file. Il precedente file di log verrà salvato , ad esempio, col nome, MioDatabase[0001-0000].4DL

Inviamo il il file MioDatabase[0001-0000].4DL a MacchinaMirror, usando 4DInternetCommands o 4DOpen.

Mentre MacchinaPrincipale continua tranquillamente a lavorare, MacchinaMirror si accorge della presenza di un nuovo file log da integrare. Esegue allora il metodo contenente il comando INTEGRATE LOG FILE che integrerà MioDatabase[0001-0000].4DL nel database. Questo è inoltre diventato così il nuovo file log.

Se quindi MacchinaPrincipale si rompe, diventando temporaneamente inutilizzabile, si può decidere di passare ad usare MacchinaMirror. Copiamo il file MioDatabase.4DL da MacchinaPrincipale a, nella posizione usuale, MacchinaMirror: questa, al solito, si accorgerà della presenza di un nuovo file di log e lo integrerà nel database, che diventerà il nuovo database da utilizzare, durante la riparazione di MacchinaPrincipale.

Riparata MacchinaPrincipale, si inseriscono su questa i file attualmente su MacchinaMirror.

I due server cominciano nuovamente a funzionare come in partenza.

Il tutto senza interruzioni lavorative e con un brevissimo tempo di attivazione del server mirror.
Codice Codice Spostamento circolare sulle ListBox
Usando le frecce per scorrere una listbox, quando la selezione arriva ad uno degli estremi della lista (inferiore o superiore che sia), una ulteriore pressione dei tasti freccia (verso il basso o verso l'alto rispettivamente) non cambia, giustamente, la riga selezionata.
Se si vuole fare in modo che, quando la riga selezionata è l'ultima, una ulteriore pressione del tasto "Freccia giù" porti la selezione al primo elemento della listbox, basta creare un pulsante nel form, associare al pulsante lo shortcut "Down Arrow" e scrivere il seguente codice nel metodo del pulsante:

Case of
  : (Form event=On Clicked )
      If (ListBox=0)
          ListBox:=1
      Else
          ListBox:=ListBox+1
          If (ListBox>Size of array(arrEsempio))
           ListBox:=1
          End if
      End if
      SELECT LISTBOX ROW(ListBox;ListBox)
End case


Stesso principio si può adottare per un pulsante associato a "Up Arrow":


Case of
  : (Form event=On Clicked )
      ListBox:=ListBox-1
      If (ListBox<=0)
          ListBox:=Size of array(arrEsempio)
      End if
      SELECT LISTBOX ROW(ListBox;ListBox)
End case

Bug Bug Termometri in 4D 2004.3
Un problema solo formale e il suo work-around.

Creando un termometro in un form, la proprietà "Fill color" risulta non accessibile.
Il problema è comunque facilmente risolvibile: basta modificare il tipo di oggetto da thermo a dial e poi di nuovo a thermometer, e la proprietà riappare.

Al momento il baco è riproducibile e sotto studio.
Comandi Comandi Il comando Get 4D folder
Il comando Get 4D folder ritorna il percorso per le cartelle utilizzate da 4D, in base al parametro passato al comando (infatti la sintassi è Get 4D folder {(folder)} ).

Le costanti utilizzabili come parametro sono:

Active 4D Folder oppure 0 (valori di default)
Licenses Folder oppure 1
Extras Folder oppure 2
4D Client Database Folder oppure 3

Nel dettaglio:

Active 4D Folder contiene i seguenti file:
le preferenze delle applicazioni o delle utility dell'ambiente 4D;
i file per il protocollo TCP/IP;
le cartelle che 4D Client scarica dal server per salvare risorse, plug-in, extras;
Solitamente il percorso della cartella è:

Su Mac OS: {Disco fisso}:Library:Application Support:4D

Su Windows: {Disco fisso}:\Documents and Settings\All Users\Application Data\4D
Su 4DClient la cartella diventa:
{Disco fisso}:\Documents and Settings\Current user\Application Data\4D
dove Current user è l'utente che ha aperto la sessione di Windows.


License Folder
Contiene i file con le licenze, si trova:
Su Mac OS : {Disco fisso}:Library:Application Support:4D:Licenses
Su Windows: {Disco fisso}:\Documents and Settings\All Users\Application Data\4D\Licenses




4D Client Database Folder (solo per le macchine client)

E' la cartella contenente tutti i file necessari al funzionamento del programma client, con le relative sottocartelle.
Il percorso è:
Su Windows: {Disco fisso}:\Documents and Settings\Current user\Application Data\4D\DatabaseName_Address

(dove Current user è l'utente che ha aperto la sessione Windows).

Su Mac OS: {Disco fisso}:Library:Application Support:4D:DatabaseName_Address




Extras Folder
Funziona solo su 4D Client e permette di conoscere il percorso alla cartella scaricata automaticamente da 4D Server contenente gli extra, tutti quei file che solitamente vogliamo siano presenti su ogni client (risorse statistiche, testi, xml, preferenze).
Il percorso è:
Su Windows: {Disco fisso}:\Documents and Settings\Current user\Application Data\4D\DatabaseName_Address\Extras

(dove Current user è sempre l'utente che ha aperto la sessione Windows).

Su Mac OS: {Disco fisso}:Library:Application Support:4D:DatabaseName_Address:Extras

Novita' Novita' 4D 2004 - Proprietà delle ListBox: Number of Static Columns
Fra le proprietà delle ListBox, quella denominata Number of Static Columns permette di impostare il numero di colonne, a partire dalla prima e comprese quelle nascoste, che non possono essere mosse in modalità User/Custom.
Quindi per impedire che le colonne vengano mosse, basta impostare questo parametro sullo stesso numero impostato su Number of Columns.

Tecniche Tecniche Spostare o copiare una cartella
Come chiesto da Michele Bertoli sul forum 4th Dimension non esiste in maniera nativa un comando 4D che permetta di copiare o spostare una cartella.
Le possibilità a questo punto sono
- creare la cartella (se serve) con CREATE FOLDER e usare un ciclo di MOVE DOCUMENT o COPY DOCUMENT per spostare i file
- usare un file batch che esegua lo spostamento via linea comando, ad esempio:

SET ENVIRONMENT VARIABLE("_4D_OPTION_HIDE_CONSOLE";"true")
LAUNCH EXTERNAL PROCESS("mycommand.bat")




1
Comandi Comandi 4D 2004 - Il comando SELECT LISTBOX ROW
In una listbox, il comando SELECT LISTBOX ROW permette di impostare la selezione per le righe il cui numero viene passsato come parametro.
La sintassi è:

SELECT LISTBOX ROW ({*; }object; position{; action})

dove
- object è l'oggetto listbox (se si passa la variabile "*" non serve, altrimenti si usa se si vuole passare il nome dell'oggetto);
- position è la riga da prendere in considerazione;
- action è un intero (facoltativo) che permette di impostare l'azione da compiere sulla riga. Per azione si intende:

Replace listbox selection (0): sostituisce la selezione attuale con quella specificata da position, è l'azione di default.
Add to listbox selection (1): la riga position viene aggiunta alla selezione corrente.
Remove from listbox selection (2): la riga position viene rimossa dalla selezione corrente.

Per scorrere direttamente via linguaggio alla riga selezionata si può usare il comando SCROLL LINES.
Per passare automaticamente alla modalità di inserimento si può usare il comando EDIT ITEM.

Novita' Novita' 4D 2004: la variabile associata ad una listbox
La variabile associata ad un oggetto list box può essere utilizzata per impostare o ottenere la selezione di righe della ListBox stessa.

La variabile è infatti un array di tipo Boolean creato e mantenuto da 4th Dimension. La dimensione dell'array è determinata dalla listbox: conterrà infatti lo stesso numero di elementi del più piccolo fra gli array associati alle colonne.

Ogni elemento dell'array è impostato su True se la corrispondente linea è selezionata, False altrimenti. 4th Dimension aggiorna automaticamente il contenuto dell'array in base alle azioni dell'utente. D'altra parte, è possibile modificare il valore degli elementi dell'array in modo da modificare la selezione nella listbox.

Questo frammento di codice, ad esempio, inverte la selezione del primo elemento della listbox lbElenco:

If (lbElenco{1} = True)
      lbElenco{1}:= False
Else
      lbElenco{1}:= True
End if


Quindi questo array non è modificabile in dimensione o in tipo.


Bug Bug [risolto] 4D Engine 2003.7 con Mac OS 10.4.4
Ho aggiornato il sistema operativo del Mac dal 10.3.9 al 10.4.4 e mi sono accorto che non è più possibile creare una applicazione inserendo il 4D Engine versione 2003.7 (resta in grigio chiaro e quindi non selezionabile). Testato il problema su altre tre macchine semore Mac con system 10.4.4 o 10.4.3 identico problema.
Unica soluzione trovata: riformattare la macchina e tornare al 10.3.9 sigh..! :(


2
Tecniche Tecniche Risolvere gli errori web nei programmi compilati
Se, scrivendo un programma destinato ad un uso via web, in interpretato funziona tutto regolarmente, mentre compilato alcune richieste ritornino il messaggio:

A runtime error occurred at line number:
When executing the method:
Unknown
Invalid parameters in an Execute command.


Il motivo di tale errore è presto spiegato: all'interno dei metodi chiamati dalle pagine html (definiti come "Available through 4DACTION, 4DMETHOD and 4DSCRIPT") è necessario inserire le definizioni:

C_TEXT($0;$1)
Comandi Comandi Il comando GET CLIPBOARD
Il comando GET CLIPBOARD, la cui sintassi è

GET CLIPBOARD (dataType; data)

dove

- dataType è ua stringa di 4 caratteri passata al comando indicante il tipo di dato presente negli appunti (case sensitive)
- data è il blob che riceve il contenuto degli appunti

permette di riempire un blob con il contenuto della clipboard.

Il risultato del comando può essere uno dei seguenti:
- i dati sono estratti correttamente dagli appunti e la variabile OK viene impostata a 1.
- gli appunti non contengono il tipo di dati specificato in dataType, la variabile OK viene settata a 0 e viene generatoun errore -102
- non c'è sufficiente memoria per eseguire il comando, OK vale 0 e viene generato un errore -108.

Codice Codice Cancellare record non bloccati
Ecco un frammento di codice da usare per cancellare dei record selezionati in un form di output, controllando che non siano bloccati.

CREATE SET(Current form table->;"SetSelezione")
USE SET("UserSet")
DELETE SELECTION(Current form table->)
USE SET("OriginalSet")

$lockedRecords:=Records in set("LockedSet")
Case of
    :($lockedRecords=1)
           $msg:=”Un record è bloccato e non può essere cancellato. Lo visualizzo?“
    :($lockedRecords>1)
           $msg:=”Alcuni record sono bloccati e non possono essere cancellati. Li visualizzo?“
    Else
           $msg:=””
End case
If ($lockedRecords#0)
    CONFIRM$msg;"Yes";"No")
    If (OK=1)
        USE SET("LockedSet")
    End if
    CLEAR SET("LockedSet")
End if
    
CLEAR SET("SetSelezione ")
FLUSH BUFFERS

Per altre informazioni sull'uso dei set consultare questa faq.

Novizi Novizi Date antecedenti
Per ottenere un valore di tipo date partire da un altro si usa il comando Add to date.
E' importante sottolineare che il comando accetta anche numeri negativi come parametro: questo significa che per portare indietro di due mesi la data contenuta nella variabile $data_d posso scrivere:

$data_d:=Add to date($data_d;0;-2;0)
Plugin Plugin Calcolo di altezza e larghezza di un testo
In 4D è incluso un plugin, 4D Chart, che non è molto considerato (forse perchè è gratis :) );in realtà oltre a fare i grafici fornisce alcuni strumenti interessanti, ad esempio per la elaborazione di testi o disegni all'interno di una immagine. Ecco un interessante esempio di utilizzo.

Usando una offscreen area di 4D Chart è possibile conoscere larghezza e altezza di una campo testo, anche multilinea.
I parametri passati sono il testo, il carattere, la dimensione, lo stile, il puntatore all'oggetto che deve ricevere la larghezza e il puntatore all'oggetto che deve ricevere l'altezza.

Ecco il metodo:

C_TEXT($Testo_t)
$Testo_t:=$1
C_STRING(255;$TipoCarattere_S)
$TipoCarattere_S:=$2
C_LONGINT($Dimensione_L;$StileTesto_L)
$Dimensione_L:=$3
$StileTesto_L:=$4
C_LONGINT($Larghezza_L;$Altezza_L)
$Larghezza_L:=0
$Altezza_L:=0
If ($Testo_t#"")
    C_LONGINT($NumeroTipoCarattere_L)
    $NumeroTipoCarattere_L:=CT Font number ($TipoCarattere_S)
    If ($NumeroTipoCarattere_L#0)
        C_LONGINT($ChartArea_L; $TestoChart_L)
        $ChartArea_L:=CT New offscreen area
        $TestoChart_L:=CT Draw text ($ChartArea_L;0;0;2048;5;$Testo_t)
        CT SET TEXT ATTRIBUTES ($ChartArea_L;$TestoChart_L;$NumeroTipoCarattere_L;$Dimensione_L;$StileTesto_L;0;0)
        C_LONGINT($sinistra_L;$alto_L;$destra_L;$basso_L)
        CT GET BOUNDARY ($ChartArea_L;$TestoChart_L;$sinistra_L;$alto_L;$destra_L;$basso_L)
        $Larghezza_L:=$destra_L-$sinistra_L `La larghzza viene trovata riducendola
        $Altezza_L:=$basso_L-$alto_L `L’altezza è corretta
        If ($Larghezza_L>0)
           Repeat
           $Larghezza_L:=$Larghezza_L-100
           CT SIZE ($ChartArea_L;$TestoChart_L;$Larghezza_L;$Altezza_L)
           CT GET BOUNDARY ($ChartArea_L;$TestoChart_L;$sinistra_L;$alto_L;$destra_L;$basso_L)
           Until ((($basso_L-$alto_L)>$Altezza_L) | ($Larghezza_L<100))
           $Larghezza_L:=$Larghezza_L+(100*Num(($basso_L-$alto_L)>$Altezza_L))
           Repeat
           $Larghezza_L:=$Larghezza_L-20
           CT SIZE ($ChartArea_L;$TestoChart_L;$Larghezza_L;$Altezza_L)
           CT GET BOUNDARY ($ChartArea_L;$TestoChart_L;$sinistra_L;$alto_L;$destra_L;$basso_L)
           Until ((($basso_L-$alto_L)>$Altezza_L) | ($Larghezza_L<20))
           $Larghezza_L:=$Larghezza_L+(20*Num(($basso_L-$alto_L)>$Altezza_L))
           Repeat
           $Larghezza_L:=$Larghezza_L-1
           CT SIZE ($ChartArea_L;$TestoChart_L;$Larghezza_L;$Altezza_L)
           CT GET BOUNDARY ($ChartArea_L;$TestoChart_L;$sinistra_L;$alto_L;$destra_L;$basso_L)
           Until ((($basso_L-$alto_L)>$Altezza_L) | ($Larghezza_L<1))
           $Larghezza_L:=$Larghezza_L+1
        Else
           `Se il testo supera i 2048 pixel, ritorna 0
        End if
        CT DELETE OFFSCREEN AREA ($ChartArea_L)
    End if
End if
$5->:=$Larghezza_L
$6->:=$ Altezza_L

Fonte: 4DToday - Michel Pourcel
File File Archiviazione o Protocollazione Immagini
Quante volte ci siamo chiesti "dove avro' messo quella ricevuta?"

io mi sono stufato e visto che ho uno scanner in grado di salvare in formato jpeg o tiff, ho fatto questa struttura:

http://www.sviluppo4d.it/4DCGI/Downloads/Archiviazione_Immagini.zip

(poteva essere fatta meglio, con piu' campi, con vari livelli di accesso, con pulsanti di stampa, con l' invio per Email, con ........ (a voglia aggiungere roba) ma cosi' ognuno se la puo' personalizzare come crede)

ovviamente richiede la presenza del QuickTime

L'ho provata anche su windows, con solo il runtime, e pare tutto ok anche con immagini da oltre un mega e spiccioli

la caratteristica e' quella di memorizzare piu' pagine con un unico riferimento

Non ho volutamente usato Subrecords e con un trigger ( on load, on save ) sul Table IMMAGINI potevo salvare le immagini su folder nel caso On Save .... e richiamarle nell On Loading ...

Ho giocato un po' con le dimensioni delle finestre ( magari non saro' stato molto preciso)

la prima cosa da fare e' individuare la cartella dove lo Scanner salva le immagini cosi' da poter creare un alias (collegamento) allo stesso livello dell'applicazione

ciao
Franco Gallai
Dhal Srl
Arezzo

P.S. : se qualcuno ci ricava un'applicazione, spero si ricordi di me

Comandi Comandi Il comando SELECTION TO ARRAY
Il comando SELECTION TO ARRAY la cui sintassi è

SELECTION TO ARRAY (field | table; array{; field2 | table2; array2; ...; fieldN | tableN; arrayN})

dove

table è la tabella da cui ottenere i record number, oppure
field sono i campi da cui ricevere i dati
array sono gli array che riceveranno i dati

crea uno o più array ricopiando i dati o i record number della selezione corrente nei vari array.

SELECTION TO ARRAY è in grado anche di caricare i valori di tabelle in relazione "a Uno" con la tabella attuale, purché sia, temporaneamente o di default, automatica.

Ogni array riceve dati dello stesso tipo, eccettuati i seguenti casi:
- se un campo testo viene copiato in uno String array, l'array resta di tipo String;
- un campo time viene copiato in un array di tipo Long Integer.

Il comando è ottimizzato per 4DServer

Dopo l'esecuzione di SELECTION TO ARRAY, sia la selezione che il record corrente non vengono modificati, ma il record corrente non risulta caricato: è dunque necessario usare un LOAD RECORD per caricarlo nuovamente.

Bug Bug 4D 2003.7 - OSX 10.3 10.4 - PRINT SETTINGS - Non imposta la stampante richiesta
In un'applicazione, sia monoutenza che server, su Mac OSX 10.3 o 10.4, non è possibile indirizzare una stampa effettuata con PRINT FORM su una stampante diversa da quella di default. Il comando PRINT SETTINGS nonostante permetta di selezionare tutte le stampanti installare sul sistema, in realtà manda poi la stampa sempre e solo su quella impostata come default da Utiltiy di Configurazione Stampanti.

Il tutto funziona invece correttamente se usiamo mac osx 10.2.8.

Ho già segnalato a ITALsoftware il bug e hanno inviato una segnalazione al supporto di 4Th Dimension.
Codice Codice Programma risolutore Sudoku: metodo ricorsivo
Per dimostrare l'equivalenza tra procedure iterative e procedure ricorsive, pubblichiamo due metodi per risolvere uno schema Sudoku 9x9x9.
Ecco la soluzione del Sudoku ricorsiva:

`Solver
$prossimo:=$1
If ($prossimo>0)
        $row:=($prossimo\9)+(1*Num(Mod($prossimo;9)>0))
        $column:=$prossimo-(($row-1)*9)
        $0:=False
        For ($i;1;9)
           arrValori{$row}{$column}:=$i
           If (CheckRules ($row;$column))
           $0:=Solver (GetNext )
           End if
           If ($0)
           $i:=12
           End if
        End for
        If ($0=False)
           arrValori{$row}{$column}:=0
        End if
Else
        $0:=True
End if


`CheckRules
$0:=True
$row:=$1
$column:=$2
$valore:=arrValori{$row}{$column}
For ($i;1;9)
        If ((($i#$row) & (arrValori{$i}{$column}=$valore)) | (($i#$column) & (arrValori{$row}{$i}=$valore)))
           $0:=False
           $i:=12
        End if
End for
If ($0)
        $firstcol:=($column-1\3)*3
        $firstrow:=($row-1\3)*3
        For ($i;1;3)
           For ($j;1;3)
           $checkRow:=$firstrow+$i
           $checkCol:=$firstcol+$j
           If ((Not(($checkRow=$row) & ($checkCol=$column))) & (arrValori{$checkRow}{$checkCol}=$valore))
           $0:=False
           $i:=12
           $j:=12
           End if
           End for
        End for
End if


`GetNext
ARRAY LONGINT($arrPosizione;0)
ARRAY LONGINT($arrQuanti;0)
For ($i;1;9)
        For ($j;1;9)
           If ((arrFissi{$i}{$j}=False) & (arrValori{$i}{$j}=0))
           INSERT ELEMENT($arrPosizione;Size of array($arrPosizione)+1)
           INSERT ELEMENT($arrQuanti;Size of array($arrQuanti)+1)
           $arrPosizione{Size of array($arrPosizione)}:=(($i-1)*9)+$j
           For ($valore;1;9)
           arrValori{$i}{$j}:=$valore
           If (CheckRules ($i;$j))
           $arrQuanti{Size of array($arrQuanti)}:=$arrQuanti{Size of array($arrQuanti)}+1
           End if
           End for
           arrValori{$i}{$j}:=0
           End if
        End for
End for
MULTI SORT ARRAY($arrQuanti;>;$arrPosizione;>)
If (Size of array($arrQuanti)>0)
        $0:=$arrPosizione{1}
Else
        $0:=0
End if


Codice Codice Programma risolutore Sudoku: metodo iterativo
Diamo seguito ad una faq precedente, pubblicando i metodi che permettono di risolvere un problema Sudoku utilizzando un sistema di programmazione iterativo.

`btElabora
ARRAY INTEGER(assudo;729)
ARRAY INTEGER(supos;81)
ARRAY INTEGER(sudo;81)

$x:=1
Repeat
    $pvar:=Get pointer("sudo"+String($x))
    If ($pvar->="")
        sudo{$x}:=0
    Else
        sudo{$x}:=Num($pvar->)
    End if
    $x:=$x+1
Until ($x>81)

$pp:=sudoarr (1)

$x:=1
Repeat
    supos{$x}:=0
    $x:=$x+1
Until ($x>81)



ARRAY INTEGER($vuoti;0)
$x:=1
$y:=1
Repeat
    
    If (sudo{$x}=0)
        INSERT ELEMENT($vuoti;$y)
        $vuoti{$y}:=$x
        $y:=$y+1
    End if
    $x:=$x+1
Until ($x>81)



$tempo:=Tickcount
    `Open window(200;200;300;350;16;"Elaborazione")
Open window(200;200;340;350;16;"Elaborazione")
sudomess

$x:=1


Repeat
    
    $posa:=supos{$vuoti{$x}}+1
    $posass:=($vuoti{$x}*9)-8
    
    If ($posa<10)
        supos{$vuoti{$x}}:=$posa
        sudo{$vuoti{$x}}:=assudo{$posass+$posa-1}
        sudomess
        If (assudo{$posass+$posa-1}#0)
          
           If ($x           $pp:=sudoarr ($vuoti{$x}+1)
           Else
           $pp:=True
           End if
           If ($pp)
           $x:=$x+1
           Else
           sudo{$vuoti{$x}}:=0
           sudomess
           If (assudo{$posass+$posa}=0)
           supos{$vuoti{$x}}:=0
           $x:=$x-1
           End if
          
           End if
        Else
           sudo{$vuoti{$x}}:=0
           sudomess
           supos{$vuoti{$x}}:=0
           $x:=$x-1
        End if
    Else
        sudo{$vuoti{$x}}:=0
        sudomess
        supos{$vuoti{$x}}:=0
        $x:=$x-1
    End if
    
    
Until ($x>Size of array($vuoti)) | ($x<1)

CLOSE WINDOW

$x:=1
Repeat
    $pvar:=Get pointer("sudo"+String($vuoti{$x}))
    $pvar->:=String(sudo{$vuoti{$x}})
    $x:=$x+1
Until ($x>Size of array($vuoti))

If ($x<1)
    ALERT("SOLUZIONE IMPOSSIBILE")
Else
    ALERT("Risolto in "+String(Int((Tickcount-$tempo)/60))+" secondi")
End if


`sudomess
riga1:=String(sudo{1})+" "+String(sudo{2})+" "+String(sudo{3})+" "+String(sudo{4})+" "+String(sudo{5})+" "+String(sudo{6})+" "+String(sudo{7})+" "+String(sudo{8})+" "+String(sudo{9})
riga2:=String(sudo{10})+" "+String(sudo{11})+" "+String(sudo{12})+" "+String(sudo{13})+" "+String(sudo{14})+" "+String(sudo{15})+" "+String(sudo{16})+" "+String(sudo{17})+" "+String(sudo{18})
riga3:=String(sudo{19})+" "+String(sudo{20})+" "+String(sudo{21})+" "+String(sudo{22})+" "+String(sudo{23})+" "+String(sudo{24})+" "+String(sudo{25})+" "+String(sudo{26})+" "+String(sudo{27})
riga4:=String(sudo{28})+" "+String(sudo{29})+" "+String(sudo{30})+" "+String(sudo{31})+" "+String(sudo{32})+" "+String(sudo{33})+" "+String(sudo{34})+" "+String(sudo{35})+" "+String(sudo{36})
riga5:=String(sudo{37})+" "+String(sudo{38})+" "+String(sudo{39})+" "+String(sudo{40})+" "+String(sudo{41})+" "+String(sudo{42})+" "+String(sudo{43})+" "+String(sudo{44})+" "+String(sudo{45})
riga6:=String(sudo{46})+" "+String(sudo{47})+" "+String(sudo{48})+" "+String(sudo{49})+" "+String(sudo{50})+" "+String(sudo{51})+" "+String(sudo{52})+" "+String(sudo{53})+" "+String(sudo{54})
riga7:=String(sudo{55})+" "+String(sudo{56})+" "+String(sudo{57})+" "+String(sudo{58})+" "+String(sudo{59})+" "+String(sudo{60})+" "+String(sudo{61})+" "+String(sudo{62})+" "+String(sudo{63})
riga8:=String(sudo{64})+" "+String(sudo{65})+" "+String(sudo{66})+" "+String(sudo{67})+" "+String(sudo{68})+" "+String(sudo{69})+" "+String(sudo{70})+" "+String(sudo{71})+" "+String(sudo{72})
riga9:=String(sudo{73})+" "+String(sudo{74})+" "+String(sudo{75})+" "+String(sudo{76})+" "+String(sudo{77})+" "+String(sudo{78})+" "+String(sudo{79})+" "+String(sudo{80})+" "+String(sudo{81})

GOTO XY(2;1)
MESSAGE(riga1)
GOTO XY(2;2)
MESSAGE(riga2)
GOTO XY(2;3)
MESSAGE(riga3)
GOTO XY(2;4)
MESSAGE(riga4)
GOTO XY(2;5)
MESSAGE(riga5)
GOTO XY(2;6)
MESSAGE(riga6)
GOTO XY(2;7)
MESSAGE(riga7)
GOTO XY(2;8)
MESSAGE(riga8)
GOTO XY(2;9)
MESSAGE(riga9)



`sudoarr
C_INTEGER($1)
C_BOOLEAN($0)
$pieno:=True
$x:=$1
Repeat
    $posass:=($x*9)-8
    If (sudo{$x}=0)
        $z:=$posass
        $zz:=1
        Repeat
           assudo{$z}:=0
           $z:=$z+1
           $zz:=$zz+1
        Until ($zz>9)
        $y:=1
        $pos:=0
        $arr:=0
        sudopos ($x)
        Repeat
           If (ChkRiga ($x;$y))
           assudo{$posass+$pos}:=$y
           $arr:=$arr+assudo{$posass+$pos}
           $pos:=$pos+1
           End if
           $y:=$y+1
        Until ($y>9)
        If ($arr=0)
           $pieno:=False
        End if
    End if
    $x:=$x+1
Until ($x>81) | ($pieno=False)
$0:=$pieno



`sudopos
C_INTEGER($1)
C_BOOLEAN($0)
$pieno:=True
$x:=$1
Repeat
    $posass:=($x*9)-8
    If (sudo{$x}=0)
        $z:=$posass
        $zz:=1
        Repeat
           assudo{$z}:=0
           $z:=$z+1
           $zz:=$zz+1
        Until ($zz>9)
        $y:=1
        $pos:=0
        $arr:=0
        sudopos ($x)
        Repeat
           If (ChkRiga ($x;$y))
           assudo{$posass+$pos}:=$y
           $arr:=$arr+assudo{$posass+$pos}
           $pos:=$pos+1
           End if
           $y:=$y+1
        Until ($y>9)
        If ($arr=0)
           $pieno:=False
        End if
    End if
    $x:=$x+1
Until ($x>81) | ($pieno=False)
$0:=$pieno


`ChkRiga
C_INTEGER($1)
C_INTEGER($2)
C_BOOLEAN($0)

$giusto:=True
$cas:=$1
$num:=$2


$inriga:=suriga*9-8
$finriga:=$inriga+8

Repeat
    If (sudo{$inriga}=$num)
        $giusto:=False
    End if
    $inriga:=$inriga+1
Until ($giusto=False) | ($inriga>$finriga)

If ($giusto)
    $incol:=sucol
    $fincol:=72+sucol
    Repeat
        If (sudo{$incol}=$num)
           $giusto:=False
        End if
        $incol:=$incol+9
    Until ($giusto=False) | ($incol>$fincol)
    
    If ($giusto)
        $inq:=suiq
        $finq:=suiq+20
        
        $x:=1
        Repeat
           If (sudo{$inq}=$num)
           $giusto:=False
           End if
           If ($x=3)
           $inq:=$inq+7
           $x:=1
           Else
           $inq:=$inq+1
           $x:=$x+1
           End if
          
        Until ($giusto=False) | ($inq>$finq)
        
    End if
    
End if

$0:=$giusto


Plugin Plugin 4D Open e l'errore -9947
Aprendo una connessione 4D Open ad un database 4D è possibile ottenere un codice di errore -9947; questo errore indica che l'opzione "Autorizza le connessioni via 4D Open" è deselezionata nelle preferenze del database.

E' possibile attivare questa opzione solo da 4th Dimension o 4D Client, ma non direttamente dalla finestra di preferenze di 4D Server.

Codice Codice Calcolo Coefficienti Sistema lineare di 3 equazioni
Informazioni sull'utilizzo del seguente metodo è spiegato nella faq Calcolo della linea di tendenza polinomiale in un grafico

    ` Method_CalcoloCoefficienti
    ` Roberto Vergani Luglio 2002

    ` Regressione Polinomiale
    ` equazione normale della parabola dei minimi quadrati.
    ` Sistema lineare di 3 equazioni nelle incognite A, B e C
    ` Calcolo dei coefficienti

C_INTEGER($X;$elementi)
C_REAL($somma_X;$somma_Y;$sommaQuadrati_X;$sommaCubo_X;$sommaQuarta_X;$somma_XY;$somma_QuadratoX_Y)
C_REAL($determinante_A;$determinante_A1;$determinante_A2;$determinante_A3)
C_REAL($elementi;$costante_A;$costante_B;$costante_C)
C_POINTER($1)
C_REAL(vReal_Coefficiente_A;vReal_Coefficiente_B;vReal_Coefficiente_C)
C_INTEGER(

$elementi:=Size of array($1->)

$somma_X:=0
$somma_Y:=0
$sommaQuadrati_X:=0
$sommaCubo_X:=0
$sommaQuarta_X:=0
$somma_XY:=0
$somma_QuadratoX_Y:=0

For ($X;1;$elementi)
        $somma_X:=$somma_X+$X
        $somma_Y:=$somma_Y+($1->{$X})
        $sommaQuadrati_X:=$sommaQuadrati_X+($X^2)
        $sommaCubo_X:=$sommaCubo_X+($X^3)
        $sommaQuarta_X:=$sommaQuarta_X+($X^4)
        $somma_XY:=$somma_XY+($X*($1->{$X}))
        $somma_QuadratoX_Y:=$somma_QuadratoX_Y+(($X^2)*($1->{$X}))
End for

    ` soluzione con il metodo di Cramer
    ` descrizione del sistema

    ` Σ_Quarta_X*a + Σ_Cubo_X*b + Σ_Quadrati_X*c = Σ_QuadratoX_Y
    ` Σ_Cubo_X*a + Σ_Quadrati_X*b + Σ_X*c = ?__XY
    ` Σ_Quadrati_X*a + Σ__X*b + $elementi*c = ?__Y

    ` ==== MATRICE DEI COEFFICIENTI

    ` Tabella mnemorica della matrice
    ` {1}{1} {1}{2} {1}{3}
    ` {2}{1} {2}{2} {2}{3}
    ` {3}{1} {3}{2} {3}{3}

ARRAY REAL($matrice;3;3) ` matrice quadrata di ordine tre (allocata come vettore di reali bidimensionale)

$matrice{1}{1}:=$sommaQuarta_X
$matrice{1}{2}:=$sommaCubo_X
$matrice{1}{3}:=$sommaQuadrati_X
$matrice{2}{1}:=$sommaCubo_X
$matrice{2}{2}:=$sommaQuadrati_X
$matrice{2}{3}:=$somma_X
$matrice{3}{1}:=$sommaQuadrati_X
$matrice{3}{2}:=$somma_X
$matrice{3}{3}:=$elementi

    ` CALCOLO DETERMINANTE DI A (non uso un loop per maggiore leggibilità)
    ` diagonali discendenti
$determinante_A:=$matrice{1}{1}*$matrice{2}{2}*$matrice{3}{3}
$determinante_A:=$determinante_A+($matrice{1}{2}*$matrice{2}{3}*$matrice{3}{1})
$determinante_A:=$determinante_A+($matrice{1}{3}*$matrice{2}{1}*$matrice{3}{2})
    ` diagonali ascendenti
$determinante_A:=$determinante_A-($matrice{3}{1}*$matrice{2}{2}*$matrice{1}{3})
$determinante_A:=$determinante_A-($matrice{3}{2}*$matrice{2}{3}*$matrice{1}{1})
$determinante_A:=$determinante_A-($matrice{3}{3}*$matrice{2}{1}*$matrice{1}{2})

If ($determinante_A#0) ` altrimenti il sistema è incompatibile o non determinato
        
           ` Sostituzione della colonna 1 con i termini noti dell'equazione
        $matrice{1}{1}:=$somma_QuadratoX_Y
        $matrice{2}{1}:=$somma_XY
        $matrice{3}{1}:=$somma_Y
        
           ` CALCOLO DETERMINANTE DI A1
           ` diagonali discendenti
        $determinante_A1:=$matrice{1}{1}*$matrice{2}{2}*$matrice{3}{3}
        $determinante_A1:=$determinante_A1+($matrice{1}{2}*$matrice{2}{3}*$matrice{3}{1})
        $determinante_A1:=$determinante_A1+($matrice{1}{3}*$matrice{2}{1}*$matrice{3}{2})
           ` diagonali ascendenti
        $determinante_A1:=$determinante_A1-($matrice{3}{1}*$matrice{2}{2}*$matrice{1}{3})
        $determinante_A1:=$determinante_A1-($matrice{3}{2}*$matrice{2}{3}*$matrice{1}{1})
        $determinante_A1:=$determinante_A1-($matrice{3}{3}*$matrice{2}{1}*$matrice{1}{2})
        
           ` Sostituzione della colonna 2 con i termini noti dell'equazione
           ` (previo ripristino della colonna 1 ai valori della Matrice A)
        $matrice{1}{1}:=$sommaQuarta_X
        $matrice{2}{1}:=$sommaCubo_X
        $matrice{3}{1}:=$sommaQuadrati_X
        
        $matrice{1}{2}:=$somma_QuadratoX_Y
        $matrice{2}{2}:=$somma_XY
        $matrice{3}{2}:=$somma_Y
        
           ` CALCOLO DETERMINANTE DI A2
           ` diagonali discendenti
        $determinante_A2:=$matrice{1}{1}*$matrice{2}{2}*$matrice{3}{3}
        $determinante_A2:=$determinante_A2+($matrice{1}{2}*$matrice{2}{3}*$matrice{3}{1})
        $determinante_A2:=$determinante_A2+($matrice{1}{3}*$matrice{2}{1}*$matrice{3}{2})
           ` diagonali ascendenti
        $determinante_A2:=$determinante_A2-($matrice{3}{1}*$matrice{2}{2}*$matrice{1}{3})
        $determinante_A2:=$determinante_A2-($matrice{3}{2}*$matrice{2}{3}*$matrice{1}{1})
        $determinante_A2:=$determinante_A2-($matrice{3}{3}*$matrice{2}{1}*$matrice{1}{2})
        
           ` Sostituzione della colonna 3 con i termini noti dell'equazione
           ` (previo ripristino della colonna 2 ai valori della Matrice A)
        $matrice{1}{2}:=$sommaCubo_X
        $matrice{2}{2}:=$sommaQuadrati_X
        $matrice{3}{2}:=$somma_X
        
        $matrice{1}{3}:=$somma_QuadratoX_Y
        $matrice{2}{3}:=$somma_XY
        $matrice{3}{3}:=$somma_Y
        
           ` CALCOLO DETERMINANTE DI A3
           ` diagonali discendenti
        $determinante_A3:=$matrice{1}{1}*$matrice{2}{2}*$matrice{3}{3}
        $determinante_A3:=$determinante_A3+($matrice{1}{2}*$matrice{2}{3}*$matrice{3}{1})
        $determinante_A3:=$determinante_A3+($matrice{1}{3}*$matrice{2}{1}*$matrice{3}{2})
           ` diagonali ascendenti
        $determinante_A3:=$determinante_A3-($matrice{3}{1}*$matrice{2}{2}*$matrice{1}{3})
        $determinante_A3:=$determinante_A3-($matrice{3}{2}*$matrice{2}{3}*$matrice{1}{1})
        $determinante_A3:=$determinante_A3-($matrice{3}{3}*$matrice{2}{1}*$matrice{1}{2})
        
           ` terna soluzione
        vReal_Coefficiente_A:=$determinante_A1/$determinante_A
        vReal_Coefficiente_B:=$determinante_A2/$determinante_A
        vReal_Coefficiente_C:=$determinante_A3/$determinante_A
        
Else
        vReal_Coefficiente_A:=0
        vReal_Coefficiente_B:=0
        vReal_Coefficiente_C:=0
End if

Info Info Calcolo della linea di tendenza polinomiale in un grafico
Ovvero: Statistica, rendere evidente la tendenza di una serie di valori in un grafico creato con 4D Chart

La graficazione di valori utilizzando le funzioni di 4D Chart ha lo scopo di permettere all’utente di valutare un fenomeno “a colpo d’occhio”.
Avviene spesso che i valori da rappresentare siano discontinui tanto da vanificare l’utilità del grafico, come in questo esempio che rappresenta la vendita giornaliera di un articolo



in casi come questo è indispensabile tracciare una linea di tendenza che renda evidente l’andamento dei valori, la stessa cosa che svolge la funzione “linee di tendenza” di Excel.
Una tra le più efficienti è la derivata polinomiale, ecco il risultato:



Solo ora si può apprezzare l’andamento e la tendenza dei valori.

L’equazione per il calcolo della derivata polinomiale, come è noto è

y = Ax^2 + Bx + C

dove A, B e C sono i coefficienti della parabola, che devono essere calcolati a partire dai valori noti.
Il calcolo dei tre coefficienti è la soluzione del sistema lineare di tre equazioni in tre incognite:




Il method Method_CalcoloCoefficienti che invio (in calce al presente messaggio) è l’implementazione in 4D della soluzione del sistema in tre incognite sopra enunciato e calcola i tre coefficienti a partire da un array di valori che viene passato in un puntatore.


Esempio d’uso

L’uso è molto semplice, faccio un esempio riferito ai valori di vendita giornaliera di un articolo.

Prima di tutto trasferire i valori delle vendite giornaliere in un array, per esempio vt_MioArrayVendite

chiamare il method passando il puntatore all’array dei valori Method_CalcoloCoefficienti (->vt_MioArrayVendite)

il method Method_CalcoloCoefficienti ritorna i tre coefficienti in tre variabili reali:

        vReal_Coefficiente_A
        vReal_Coefficiente_B
        vReal_Coefficiente_C

che permettono di calcolare i valori della curva di tendenza con l’equazione standard della parabola dei minimi quadrati.

Y = (vReal_Coefficiente_A*(X^2))+(vReal_Coefficiente_B*X)+vReal_Coefficiente_C

Nota bene: nel caso in cui i valori noti (passati al method) siano incongruenti e generino un sistema di equazioni indeterminato o impossibile, il valori dei tre coefficienti vengono ritornati tutti a zero. Quindi questa eventualità va prevista e gestita.

Ottenuti i coefficienti, calcoliamo i valori della curva e li memorizziamo in un nuovo array vt_Tendenza

If ((vReal_Coefficiente_A+vReal_Coefficiente_B+vReal_Coefficiente_C)#0) ` se è stato possibile calcolare i coefficienti (sistema determinato)

For ($X;1; Size of array(vt_MioArrayVendite))
        vt_Tendenza{$X):=(vReal_Coefficiente_A*($X^2))+(vReal_Coefficiente_B*$X)+vReal_Coefficiente_C
End for

Else
` non è possibile creare una linea di tendenza
End if

Ora possiamo rappresentare nel grafico sia i valori di vendita (array vt_MioArrayVendite) sia la curva di tendenza (array vt_Tendenza).

Naturalmente la curva così ottenuta premette anche di tracciare una teorica tendenza futura, prolungando i valori dell’asse X.



Clic qui per Metodo Calcolo Coefficienti Sistema lineare di 3 equazioni
Info Info Conversione da 2003 a 2004 per gli output form
Dalla versione 2004 è presente una nuova proprietà per i form, "With Constraints", che permette la gestione dei form ridimensionabili.

Nella conversione dalla versione 2003 alla 2004 tale proprietà risulta selezionata di default per i form.

Un problema che può capitare, in base alla poszione degli oggetti, è che il pulsante di chiusura venga nascosto dalla barra di scorrimento: per riportare tutto alla situazione originale basta disattivare tale proprietà.

Codice Codice Riempire una combo su una pagina web
Dal forum tecnico italiano pubblico il procedimento da seguire per riempire una combo su una pagina web.

Nella pagina web viene inserito il seguente testo:

&lt;!--#4DSCRIPT/Web_CaricaArray--&gt; : The method does not exist. : The method does not exist. : The method does not exist.

Ecco il metodo Web_CaricaArray

C_TEXT($0;$1)
ALL RECORDS([Pazienti])
ORDER BY([Pazienti];[Pazienti]Cognome)
$menu:=Char(1)+"<"+"select name=\"SelectableList\">\r"
FIRST RECORD([Pazienti])
While (Not(End selection([Pazienti])))
      $menu:=$menu+"<"+"option value=\""+[Pazienti]Cognome+"\">"+[Pazienti]Cognome+"\r"
      NEXT RECORD([Pazienti])
End while
$menu:=$menu+""
$0:=$menu

Due cose da notare:
- il value è quello che ritorna dopo che l'utente ha selezionato qualcosa
- il Char(1) come primo carattere iniziale segnala a 4d che il testo che si invia è codice html (altrimenti lui sostituisce i caratteri < con il simbolo & lt; per mostrarli bene a video come testo).

Info Info Da Mac a Windows: alternativa a 4D Transporter
Per chi vuole "trasportare" le proprie applicazioni ed i dati dal formato 4D Mac a Windows esiste il tool 4D Transporter.

Tuttavia, come forse qualcuno avra' gia' notato, per utilizzare questo prodotto in ambiente Mac OSX e' necessario aver installato il sottosistema Classic.

Per chi, come me, non voglia installare il sottosistema Classic e' disponibile il programma ResTransporter, rilasciato con licenza freeware.

Lo potete scaricare dalla pagina:

http://www.softrobots.de/dimension/restransporter/index.html

oppure direttamente attraverso il link:

http://www.softrobots.de/Dateien/ResTransporterX.sit

Buona conversione!

Tecniche Tecniche Icona personalizzata nelle applicazioni compilate
Con 4th Dimension è possibile realizzare applicazioni integrando un particolare runtime: ecco le istruzioni per utilizzare, dalla versione 2004, un'icona personalizzata.

L'icona deve avere lo stesso nome della file della struttura interpretata (il normale file che finisce in .4DB): basta mettere il file dell'icona nella stessa cartella e 4d la userà automaticamente quando si genererà l'applicativo finale.

- Per Windows, il file dell'icona ha estensione .ico extension
- Per Mac OS, il file dell'icona è di tipo .icns

Plugin Plugin Controllare la memoria disponibile
Quando si lavora con i blob, benché la loro dimensione può arrivare a 2GB, è necessario però controllare di avere memoria libera a sufficienza. Occorre farlo anche quando si utilizzano array prima di caricare una grande quantità di dati, perchè l'operazione potrebbe non andare a buon fine per problemi di spazio.

A questo scopo, prima di fare operazioni di un certo tipo, si può controllare la disponibilità usando questo comando del plugin gratuito 4D Pack:

AP AVAILABLE MEMORY (MemoriaTotale; MemoriaFisica; MemoriaDisponibile; StackDisponibile)

Il comando AP AVAILABLE MEMORY ritorna le informazioni in bytes sulla memoria installata (totale e fisica) e su quella disponibile a 4D del computer corrente. Ritorna anche la quantità di spazio libero nello stack per il processo corrente.
Codice Codice Spostamento delle immagini in Picture Library
Non tutti hanno l'insider per spostare le immagini da un applicativo all'altro, questo metodo consente di copiarle da e verso un Table

ARRAY LONGINT($Riferimento;0)
ARRAY STRING(80;$Nomi;0)
PICTURE LIBRARY LIST($Riferimento;$Nomi)

If (True)
  ALL RECORDS([IMMAGINI])
  While (Not(End selection([IMMAGINI])))
    $i:=Find in array($Riferimento;[IMMAGINI]Riferimento)
    If ($i<1)
      SET PICTURE TO LIBRARY([IMMAGINI]Immagine;[IMMAGINI]Riferimento;[IMMAGINI]Nome)
    End if
    NEXT RECORD([IMMAGINI])
  End while
Else
  C_PICTURE($Picture)
  READ WRITE([IMMAGINI])
  
  If (Size of array($Riferimento)>0)
    For ($i;1;Size of array($Riferimento))
      GET PICTURE FROM LIBRARY($Riferimento{$i};$Picture)
      QUERY([IMMAGINI];[IMMAGINI]Riferimento=$Riferimento{$i})
      If (Records in selection([IMMAGINI])=0)
        CREATE RECORD([IMMAGINI])
        [IMMAGINI]Riferimento:=$Riferimento{$i}
        [IMMAGINI]Nome:=$Nomi{$i}
        [IMMAGINI]Immagine:=$Picture
        SAVE RECORD([IMMAGINI])
        UNLOAD RECORD([IMMAGINI])
      End if
    End for
  End if
End if
File File Elenco Comuni Italiani
Elenco dei comuni italiani, con CAP, Provincia, Codice Fiscale, Codice Istat, Codice Inps.

ComuniItaliani
Novizi Novizi Iniziare a programmare con 4D
Segnaliamo un un'unica faq i link alle risorse con cui iniziare a farsi un'idea del funzionamento di 4D.

La versione demo di 4th Dimension è scaricabile dal sito di 4D: fra i download proposti, scegliere la versione del file "4th Dimension vvvv" (mentre scriviamo vvvv sta per 2004) per la propria piattaforma: quella scaricata è la versione più recente del sistema di sviluppo e, se non avete acquistato 4D, potrete utilizzarlo tranquillamente e totalmente in modalità "Demo", l'unica limitazione è data dalla dimensione del progetto (come numero di tabelle/archivi/file e numero di method/procedure/scripts). Se siete studenti, insegnanti o organizzazioni no-profit, potete ottenere una versione di 4D gratis o ad un prezzo simbolico.

Scaricato il file siete già pronti a partire: vediamo come fare ciò che vogliamo fare.

Su questo sito si trova una sorta di "compendio breve" su 4D, inviato da Roberto Vergani, ecco i link:

Prima introduzione a 4d: EVENTI e gerarchia esecuzione

Prima introduzione a 4d: FILE E CAMPI

Prima introduzione a 4d: FORMS [schermate]

Prima introduzione a 4d: METODI [procedure, scripts]

Prima introduzione a 4d: SUBTABLE

Un manuale completo, Jumpstart 4D, in inglese, è segnalato in questa faq.

Codice Codice Calcolo lunghezza massima dei testi nei campi
Quando si importano in 4d file di testo la creazione delle tabelle viene fatto in automatico le lunghezze dei campi Alpha è di default 80. Ecco un metodo da usare per analizzare il contenuto di tutte le tabelle di un database per trovare per i campi testo o alpha la stringa più lunga (con l'esempio più lungo) e quante volte il campo è usato.

`Nexus srl www.nexusonline.it
`Umberto Migliore 29 nov 2005
READ ONLY(*)
$doc:=Create document("")
If (ok=1)
  $cr:=Char(Carriage return )
  $tab:=Char(Tab )
  
  For ($t;1;Count tables)
    $table_ptr:=Table($t)
    DEFAULT TABLE($table_ptr->)
    SEND PACKET($doc;Table name($t)+$tab+String(Records in table)+" recs"+$cr)
    $quanti:=Count fields($t)
    ARRAY LONGINT($quantiUsati_al;$quanti)
    ARRAY LONGINT($maxLun_al;$quanti)
    ARRAY TEXT($maxTesto_at;$quanti)
    For ($f;1;$quanti)
      $quantiUsati_al{$f}:=0
      $maxLun_al{$f}:=0
      $maxTesto_at{$f}:=""
    End for
    
    ALL RECORDS
    While (Not(End selection))
      If (Mod(Selected record number;100)=0)
        MESSAGE(Table name($t)+": "+String(Selected record number)+"/"+String(Records in table))
      End if
      
      For ($f;1;$quanti)
        $field_ptr:=Field($t;$f)
        If ((Type($field_ptr->)=Is Alpha Field ) | (Type($field_ptr->)=Is Text ))
          $text:=u_EliminaSpazi ($field_ptr->)
          $len:=Length($text)
          If ($len>0)
           $quantiUsati_al{$f}:=$quantiUsati_al+1
           If ($len>$maxLun_al{$f})
           $maxLun_al{$f}:=$len
           $maxTesto_at{$f}:=$text
           End if
          End if
        Else
          $maxLun_al{$f}:=-1
        End if
        
      End for
      
      NEXT RECORD($table_ptr->)
    End while
    
    For ($f;1;$quanti)
      SEND PACKET($doc;Field name($t;$f)+$tab+String($quantiUsati_al{$f})+$tab+String($maxLun_al{$f})+$tab+$maxTesto_at{$f}+$cr)
    End for
    
  End for
  CLOSE DOCUMENT($doc)
End if
Tecniche Tecniche Menu Quit su Mac OS X
4D 2004 su Mac OS X si occupa di spostare automaticamente il menu Quit dal suo posto nel menu File sotto il menu Application del Mac OS X.

Basta assegnargli un metodo e lasciare l'azione automatica Quit.
Info Info Le righe di output che scompaiono
Tra le novità "nascoste", nel senso di poco segnalate, della versione 2004, ne esiste una piccolissima riguardante i form di output che può in certi rari casi diventare un problema: andiamo con ordine.

Fino alla versione 2003 le righe selezionate dei form di output venivano "colorate" di nero, utilizzando la tecnica della inversione per mostrare i colori.

Dalla versione 2004 è possibile invece utilizzare il colore di selezione di sistema: queste opportunità, se da un lato risulta molto elegante, dall'altro può rivelarsi infernale se il colore di selezione di sistema è lo stesso di quello delle scritte del form di output: in questo caso infatti il testo delle righe selezionate "scompare", poiché uniforme al colore di selezione.

Per poter utilizzare il sistema di "colorazione" precedente basta andare nelle proprietà del form, nella categoria "Form properties" e deselezionare la voce System Highlight Color
Novizi Novizi Control flow nell'esecuzione di un metodo: profondità
Abbiamo analizzato i vari metodi di controllo di flusso di 4D. Le varie strutture sono variamente combinabili.

È interessante notare come il limite di profondità nell'inserire tali strutture (If/While/For/Case of/Repeat) è di 512 livelli.

Novizi Novizi Control flow nell'esecuzione di un metodo: ramificata
Una struttura ramificata (anche se il termine inglese Branching rende meglio l'idea) controlla il verificarsi o meno di una condizione per far prendere al codice "strade" diverse, valutando se una certa espressione restituisce un valore TRUE o FALSE.

Un esempio di struttura ramificata è dato dalla sequenza If...Else...End if, dove il flusso del programma viene suddiviso in più percorsi.

Altro esempio è dato dall'insieme di Case of...Else...End case, che invece suddivide l'esecuzione in più percorsi.

Novizi Novizi Control flow nell'esecuzione di un metodo: ciclica
Una struttura ciclica viene utilizzata per eseguire più volte una certa quantità di codice. Per tale scopo, le strutture linguistiche usate sono:

While...End while

Repeat...Until

For...End for

I cicli sono controllati in due modi: o in base al verificarsi di una condizione o specificando il numero di cicli (anche se in realtà è possibile scmbiare i due sistemi senza problemi).

Quindi la differenza sostanziale sta nel fatto che i While e i Repeat vengono eseguiti un numero indefinito di volte, mentre i cicli For vengono eseguiti un numero prestabilito di volte.

Novizi Novizi Control flow nell'esecuzione di un metodo: sequenziale
Per quanto un metodo possa essere scritto in maniera semplice o complessa, i sistemi di scrittura del codice 4D (sistemi di controllo di flusso) sono sempre tre:

Sequenziale

Ramificato

Ciclico

cominciamo a entrare nel dettaglio.

Il sistema sequenziale è dato dalla esecuzione lineare, sequenziale appunto, di una serie di istruzioni 4th Dimension:

OUTPUT FORM([People]; "Listing")
ALL RECORDS([People])
DISPLAY SELECTION([People])

Si può anche usare una istruzione o un comando in un'unica riga, è sempre programmazione sequenziale:

[People]Last Name:=Uppercase([People]Last Name)

Comandi Comandi Il comando SET BLOB SIZE
Il comando SET BLOB SIZE, la cui sintassi è:

SET BLOB SIZE (blob; size{; filler})

permette di specificare una nuova dimensione size per il blob blob passato come parametro.
Il comando è ad esempio usato per svuotare il contenuto di un blob.

Il parametro opzionale filler permette di specificare, in caso di aumento di dimensione del blob, il codice ascii del carattere da utilizare per "riempire" i byte aggiunti.
Tecniche Tecniche Uso di Get pointer con gli array
Il comando Get pointer permette di ottenere un puntatore ad una variabile (processo o interprocesso) il cui nome viene passato come parametro al comando.
Per ottenere puntatori a campi si utilizza Field, per i puntatori alle tabelle usare Table.

Per gli array, è possibile usare i puntatori per operazioni del tipo INSERT ELEMENT o DELETE ELEMENT. Inoltre si possono passare puntatori ad elementi ben precisi degli array, ad esempio:

Get pointer($ArrName+"{3}")

mentre non è possibile puntare ad elementi variabili, come:

Get pointer($ArrName+"{myVar}")

Il comando risulta molto utile per gestire griglie di elementi; se ho una griglia di 5x10 variabili v1, v2, ...., v50, posso settare i valori così:

For ($vlVar;1;50)
      $vpVar:=Get pointer("v"+String($vlVar))
      $vpVar->:=""
End for

Info Info Vantaggi dell'overclocking su applicazioni 4th Dimension
Una delle attività preferite degli smanettoni su Windows è sicuramente quella dell'overclocking, ovvero l'agire sui parametri (ma a volte anche in maniera fisica) della scheda madre.
Alcune delle tecniche più utilizzare sono la modifica dei voltaggi di funzionamento e la variazione delle frequenze di funzionamento insieme ai fattori moltiplicativi di queste frequenze.
Tali azioni sono effettuabili solo su hardware ad alta affidabilità e comportano problemi accessori, come lo stress a cui vengono sottoposti i vari componenti e il surriscaldamento che tali modifiche comportano, per non dire la possibile instabilità della macchina.
Ma allora quale vantaggio c'è nell'overclockare un computer?
Semplicemente nella possibilità di ottenere un aumento di prestazioni fino al 50% utilizzando la stessa componentistica.
Uno dei sistemi di valutazione più utilizzati per calcolare l'efficienza di un overclock è il calcolo di 1Mb o di 32MB di cifre decimali per "p greco".
Ecco il link dove scaricarlo.

Per testare la velocità di 4D con vari hardware uso solitamente il codice di questa faq su 500.000 record: ecco alcuni risultati.

Alcuni dati di partenza:
create 6361tick
withDialog 11911tick
export 1083tick

Sostituendo il processore con uno avente un 6% di MHz in più e la scheda madre con una avente un bus più veloce del 20% ecco l'incremento dei risultati:

create 5299tick
withDialog 10817tick
export 986tick

Da notare che questi dati sono praticamente identici a quelli ottenuti da un server di ultimissima generazione zeppo di RAM e doppio processore (certo, viene usato solo uno dei quattro processori visto dal sistema operativo, speriamo in 4D 2007...), meno che per il test "withDialog", visto che la scheda video non è dedicata al refresh dello schermo.

Ora, abbassando il fattore moltiplicativo del processore, alzandone di conseguenza l'FSB, e aumentando il voltaggio di memoria e scheda video, i risultati cambiano così:

GLOBAL mm:ss:tt [500000 Inter] 00:05:09, 18557tick
create 00:00:54, 3256tick
withDialog 00:01:35, 5708tick
export 00:00:13, 788tick
import 00:01:14, 4446tick
index 00:00:53, 3187tick
qryMedium 00:00:01, 61tick
qryLarge 00:00:01, 55tick
qryDouble 00:00:05, 309tick
orderSingle 00:00:00, 26tick
orderDouble 00:00:12, 721tick

Un incremento di prestazioni anche del 20% rispetto alla configurazione senza overclock!
Plugin Plugin Conversione dei data types tra 4D Server e SQL
Il driver ODBC per 4D Server converte i tipi di dati 4D in ben precisi tipi di dati SQL secondo il seguente schema:

Alpha SQL_VARCHAR
Text SQL_LONGVARCHAR
Real SQL_DOUBLE
Integer SQL_SMALLINT
Long Integer SQL_INTEGER
Date SQL_DATE
Time SQL_TIME
Boolean SQL_BIT
Picture SQL_LONGVARBINARY
Subtable N/A
BLOB N/A

Questa invece la conversione dei tipi da una fonte ODBC SQL verso 4D Server:

SQL_VARCHAR Alpha
SQL_CHAR Alpha
SQL_LONGVARCHAR Text
SQL_REAL Real
SQL_DOUBLE Real
SQL_DECIMAL Real
SQL_SMALLINT Integer
SQL_TINYINT Integer
SQL_INTEGER Long Integer
SQL_LONGVARBINARY Picture
SQL_DATE Date
SQL_TIMESTAMP Date
SQL_TIME Time
SQL_BIT Boolean

Codice Codice Aprire le porte seriali con Serial ToolKit for 4D
A differenza di SET CHANNEL, che apre le porte seriali passando il numero di porta COM da usare, Serial ToolKit apre le porte in base al numero di COM installate, indipendentemente dal loro indirizzo: il che significa che potrei dover passare l'indirizzo 2 a Serial ToolKit per aprire la COM1.
Per ottenere il mumero di porta corretto si usa la funzione STK_CountPorts per conoscere il numero di porte installate, e un ciclo ($i da 1 a STK_CountPorts) in cui chiedere a STK_GetIndPort se $i vale il numero corrispondente alla porta COM che ci interessa.

Comandi Comandi Mostrare la posizione di un file o di una cartella
Dalla 2004.1 è disponibile il comando SHOW ON DISK per mostrare la posizione di un file o di una cartella.

Il comando apre proprio la cartella del Finder su Mac o dell'Explorer su Windows.

Il parametro passato può essere il nome di un file o di una cartella.

SHOW ON DISK(“C:\\MiaCartella\\MioDocumento.txt”)
SHOW ON DISK(“C:\\MiaCartella\\CartellaInterna)

SHOW ON DISK(“Macintosh HD:MiaCartella:MioDocumento.txt”)
SHOW ON DISK(“Macintosh HD:MiaCartella:CartellaInterna")

Se si indica una cartella il Finder/Explorer ne mostrano la posizione; nel caso in cui la si volesse già aperta passare un secondo parametro *.

SHOW ON DISK(“C:\\MiaCartella\\CartellaInterna”;*)
SHOW ON DISK(“Macintosh HD:MiaCartella:CartellaInterna";*)

Comandi Comandi Il verso di esecuzione dei comandi di comunicazione interprocesso
I comandi GET PROCESS VARIABLE, SET PROCESS VARIABLE and VARIABLE TO VARIABLE permettono di far comunicare fra di loro i processi.

Tali comandi sono utilizzabili sia in versione monoutente che in modalità Client/Server.

In quest'ultimo caso, è importante ricordare come le operazioni di lettura o scrittura avvengono solo nel verso "dal client al server": non è dunque possibile far comunicare direttamente fra di loro due client o eseguire le operazioni di lettura/scrittura dal server verso il client.

Info Info Il numero di tabella
Molto spesso, ad esempio per le comunicazioni via 4D Open, risulta necessario conoscere il numero di tabella al di fuori di un metodo.

Fra i sistemi possibili possiamo citare la possibilità di scrivere all'interno della voce Watch del Runtime Explorer "Table(->[Nome_tabella])"; oppure andare alla voce "Info su..." del menu "Help" in modalità Design, e alla voce "Database" vedere il numero delle varie tabelle.

Dalla versione 2004, il numero di tabella si trova anche nella struttura, nella finestra di Proprietà della Tabella.

Info Info Sfondo bianco per un'immagine
Ilaria Giagnoni ha segnalato una interessante info per la stampa delle picture: per ottenere delle immagini con sfondo bianco basta che la variabile contenente l'immagine sia impostata come "enterable".

Codice Codice Esporta descrizione struttura di una Tabella
C_LONGINT($Table_l;$i)
DEFAULT TABLE([Strutture])

$Table_l:=Table(Current default table)
$doc:=Create document("")
If (ok=1)
  SEND PACKET($doc;"TABLE "+Table name($Table_l)+Char(Carriage return))
  For ($i;1;Count fields($Table_l))
    $t:=Type(Field($Table_l;$i)->)
    Case of
      : (($t=Is LongInt ) | ($t=Is Integer ))
        $tipo:="Long"
      : ($t=Is Real )
        $tipo:="Real"
      : ($t=Is Alpha Field )
        $tipo:="Alfa"
      : ($t=Is Text )
        $tipo:="Text"
      : ($t=Is Boolean )
        $tipo:="Bool"
      : ($t=Is Date )
        $tipo:="Date"
      : ($t=Is Time )
        $tipo:="Time"
      : ($t=Is Picture )
        $tipo:="Pict"
      Else
        $tipo:="blob "+String($t)
    End case
    SEND PACKET($doc;Char(Tab)+$tipo+Field name($Table_l;$i)+Char(Carriage return))
    
  End for
  CLOSE DOCUMENT($doc)
End if

Tecniche Tecniche Merge di due file dati
Franz chiede:
Qualche esperto mi saprebbe dire come unire due files di dati in un unico file (subrecords compresi) in modo da avere un unico archivio dati ? Grazie !

Risposta:
Usando le due procedure dalla Faq Rapido Trasferimento Dati l'operazione è molto semplice, posto di avere due base dati con la stessa struttura.

Quindi, abbiamo le due base dati A e B e vogliamo trasferire tutto da B accodandolo ad A, incluso di subfile, immagini e Blob.

1. Inseriamo nella struttura le due procedure, DB_Esporta e DB_Importa
2. Teniamo la stessa struttura e le due base dati nella stessa cartella
3. Apri la base dati B ed eseguiamo DB_Esporta (crea vari file di passaggio)
4. Apri la base dati A ed esegui DB_Importa (trova i file e li legge)

1
Codice Codice Apertura di un file con 4D 2003
Le funzioni AP ShellExecute e AP Sublaunch, inserite o modificate a partire dal 4D Pack della versione 6.8.2, permettono di lanciare applicazioni esterne o aprire file. Ad esempio:

$err:=AP ShellExecute ("C:\Test.doc")

oppure

$err:=AP ShellExecute ("MacHD:Test.doc")

Ma si può anche fare in modo che il file venga aperto da una applicazione specifica, utilizzando ad esempio AP Sublaunch in questo modo:

$err:=AP Sublaunch ("C:\\Programmi\\Microsoft Office\\OFFICE11\\WINWORD.EXE c:\\Test.doc")

Per il lancio di applicazioni esterne con la versione 2004 vi rimando a questa faq.


Per l'uso di "\\" per il passaggio di percorsi Windows vi rimando a questa faq.

Per aprire una voce di Pannello di Controllo usando AP Sublaunch si può vedere questa faq.
Codice Codice Calcolo della varianza
Sempre da Math4D pubblichiamo il metodo per il calcolo della varianza di una serie, dove la serie è passata al metodo come puntatore ad un array. La varianza è data dalla formula:

Sommatoria(x^2)/n - (Sommatoria(x)/n)^2

Ecco il metodo:

$k:=Size of array($1->)
$x2:=0
$x:=0

For ($i;1;$k)
      $x2:=$x2+($1->{$i}^2)  `Sommatoria(x2)
      $x:=$x+$1->{$i}  ` Sommatoria(x)
End for

$0:=$k*$x2  ` Dimensione array*Sommatoria(x^2)
$0:=$0-($x^2)  ` nSommatoria(x^2) - (Sommatoria(x))2
$0:=$0/($k^2)  ` (nSommatoria(x2) - (Sommatoria(x))2) / n2


Tecniche Tecniche Arrotondamenti dinamici per eccesso o per difetto
Il problema: trovare un sistema che consenta di effettuare degli arrotondamenti "intelligenti", cioé non allo zero, ma al 5 più vicino

- se ho 23,61 deve restituire 23,60
- se ho 23,68 deve restituire 23,70
- se ho 23,66 deve restituire 23,65

Una formula che risolva il problema con un'unica istruzione è:

Round($numero_r/0,05;0)*0,05

Codice Codice Ottenere l'indirizzo IP
Il modo più semplice è usare il comando IT_MyTCPAddr del plugin 4d Internet Command (gratuito e incluso con 4d).

Il comando richiede due parametri di tipo Stringa, dove il comando ritorna il numero IP corrente e la corrispondente maschera di Subnet.
Ecco un esempio di utilizzo:

C_TEXT($indirizzo_t;$subnet_t)
C_LONGINT($errorCode_l)
$errorCode_l := IT_MyTCPAddr ($indirizzo_t; $subnet_t)
If ($errorCode_l =0)
  ALERT("IP address: " + $indirizzo_t + "\n" + "Subnet Mask: "+ $subnet_t)
End if

Questo è l'indirizzo con cui siete identificati nella rete locale; se però volete sapere qual'è l'indirizzo con cui siete visti su Internet il problema è più ampio. Se usate un modem vi viene assegnato un numero tutte le volte che telefonate; altrimenti nella maggior parte dei casi è il numero con cui il router accede ad internet.

Per saperlo da programma è necessario utilizzare un server esterno che ci dica come ci vede: ad esempio potete usare il server Nexus WS Server con una semplice chiamata Web Service.
Ecco un esempio con il codice in 4d per ottenere l'indirizzo ip pubblico.
Codice Codice Conversione da numero arabo a numero romano
Per completare l'argomento iniziato con la faq precedente ecco il metodo di conversione di un numero in notazione araba in notazione romana.


C_STRING(255;$0;$risultatoRomano_S)
C_INTEGER($1;$numeroArabo_I)
C_BOOLEAN($finito_B)
C_STRING(2;$decina_S;$cinquina_S;$unità_S)
C_STRING(4;$prefissi_S)

    `inizializzazione
$finito_B:=False
$numeroArabo_I:=$1
$risultatoRomano_S:=""
MATHERROR:=0
    `calcolo
Repeat
      $num:=$numeroArabo_I
      Case of
           : ($num>999)
           $prefissi_S:="M"*Num(Substring(String($numeroArabo_I);1;1))
           : ($num>99)
           $unità_S:="C"
           $cinquina_S:="D"
           $decina_S:="M"
           : ($num>9)
           $unità_S:="X"
           $cinquina_S:="L"
           $decina_S:="C"
           Else
           $unità_S:="I"
           $cinquina_S:="V"
           $decina_S:="X"
      End case
      If ($num<1000)
           $num:=Num(Substring(String($numeroArabo_I);1;1))
           $prefissi_S:=""
           Case of
           : ($num<4)
           $prefissi_S:=$unità_S*$num
           : ($num=4)
           $prefissi_S:=$unità_S+$cinquina_S
           : ($num=5)
           $prefissi_S:=$cinquina_S
           : ($num<9)
           $prefissi_S:=$cinquina_S+($unità_S*($num-5))
           : ($num=9)
           $prefissi_S:=$unità_S+$decina_S
           End case
      End if
      $risultatoRomano_S:=$risultatoRomano_S+$prefissi_S
      $numeroArabo_I:=Num(Substring(String($numeroArabo_I);2))
      $finito_B:=($numeroArabo_I<=0)
Until ($finito_B=True)

$0:=$risultatoRomano_S
Codice Codice Conversione da numero romano a numero arabo
Pubblichiamo di seguito una versione tradotta del metodo "MATH_RomainVersArabe" presente nella libreria Math4Dv2. il metodo prende come parametro una stringa in forma "romana" e restituisce l'equivalente in numeri arabi:

C_STRING(80;$1)
C_INTEGER($0;$risultatoArabo_L)

    `inizializzazione
$numeroRomano_S:=$1
$risultatoArabo_L:=0
MATHERROR:=0

    `calcolo
Repeat
  Case of
    : ($numeroRomano_S[[1]]="M")
      $risultatoArabo_L:=$risultatoArabo_L+1000
      $numeroRomano_S:=Substring($numeroRomano_S;2)

    : ($numeroRomano_S[[1]]="D")
      $risultatoArabo_L:=$risultatoArabo_L+500
      $numeroRomano_S:=Substring($numeroRomano_S;2)

    : ($numeroRomano_S[[1]]="C")
      Case of
        : (Length($numeroRomano_S)=1)
          $risultatoArabo_L:=$risultatoArabo_L+100
          $numeroRomano_S:=""
        : ($numeroRomano_S[[2]]="M")
          $risultatoArabo_L:=$risultatoArabo_L+900
          $numeroRomano_S:=Substring($numeroRomano_S;3)
        : ($numeroRomano_S[[2]]="D")
          $risultatoArabo_L:=$risultatoArabo_L+400
          $numeroRomano_S:=Substring($numeroRomano_S;3)
        Else
          $risultatoArabo_L:=$risultatoArabo_L+100
          $numeroRomano_S:=Substring($numeroRomano_S;2)
        End case

    : ($numeroRomano_S[[1]]="L")
      $risultatoArabo_L:=$risultatoArabo_L+50
      $numeroRomano_S:=Substring($numeroRomano_S;2)

    : ($numeroRomano_S[[1]]="X")
        Case of
        : (Length($numeroRomano_S)=1)
          $risultatoArabo_L:=$risultatoArabo_L+10
          $numeroRomano_S:=""
        : ($numeroRomano_S[[2]]="C")
          $risultatoArabo_L:=$risultatoArabo_L+90
          $numeroRomano_S:=Substring($numeroRomano_S;3)
        : ($numeroRomano_S[[2]]="L")
          $risultatoArabo_L:=$risultatoArabo_L+40
          $numeroRomano_S:=Substring($numeroRomano_S;3)
        Else
          $risultatoArabo_L:=$risultatoArabo_L+10
          $numeroRomano_S:=Substring($numeroRomano_S;2)
        End case

    : ($numeroRomano_S[[1]]="V")
      $risultatoArabo_L:=$risultatoArabo_L+5
      $numeroRomano_S:=Substring($numeroRomano_S;2)

    : ($numeroRomano_S[[1]]="I")
      Case of
        : (Length($numeroRomano_S)=1)
          $risultatoArabo_L:=$risultatoArabo_L+1
          $numeroRomano_S:=""
        : ($numeroRomano_S[[2]]="X")
          $risultatoArabo_L:=$risultatoArabo_L+9
          $numeroRomano_S:=Substring($numeroRomano_S;3)
        : ($numeroRomano_S[[2]]="V")
          $risultatoArabo_L:=$risultatoArabo_L+4
          $numeroRomano_S:=Substring($numeroRomano_S;3)
        Else
          $risultatoArabo_L:=$risultatoArabo_L+1
          $numeroRomano_S:=Substring($numeroRomano_S;2)
      End case
    Else
      MATHERROR:=-1  `numero inesistente
      $numeroRomano_S:=""
      $risultatoArabo_L:=0
  End case
Until ($numeroRomano_S="")

$0:=$risultatoArabo_L




Autori, redattori e collaboratori per Math4Dv2:
Jacques Bossy
Philip Burns
Olivier Deschanels
Marc Duc-Jacquet
Bernard Escaich
Antoine Galmiche
Micaël Germann
Teddy Linet
Frédéric Quoirez
Michel Saiz
Robert Van Loo


Codice Codice Leggere i tag ID3 di un file MP3
Tutto quello che serve per ascoltare un file MP3 è un lettore di file MP3! All'interno di questi file sono celate delle informazioni suppletive sul brano.
Un esempio di struttura di tali tag:

000-002 03 bytes TAG ID -> "TAG"
003-032 30 bytes Titolo del brano
033-062 30 bytes Nome dell'interprete
063-092 30 bytes Album
093-096 04 bytes Anno
097-127 30 bytes Commenti
128-128 01 byte Tipo

Utilizzando i comandi Open document, SET DOCUMENT POSITION e RECEIVE PACKET possiamo scriverci un metodo che inserisce in un array (passato come secondo parametro) i tag ID3 di un file MP3 (il cui percorso completo viene passato come primo parametro).

C_TEXT($1) `percorso del file
C_POINTER($2) `puntatore all'array con 7 posti che riceve i tag
C_TEXT($MyText)
C_STRING(3;$Tag)
C_LONGINT($MyOffset)
$RefDoc:=Open document($1;"")
If (ok=1)
      `Leggo gli ultimi 128 bytes del file MP3
      SET DOCUMENT POSITION($RefDoc;-128;2)
      RECEIVE PACKET($RefDoc;$MyText;500)
      CLOSE DOCUMENT($RefDoc)
  
      $Tag:=$MyText[[1]]+$MyText[[2]]+$MyText[[3]]
      If ($Tag="TAG") `c'è un tag MP3
           $2->{1}:=EliminaSpazi (Substring($MyText;4;30))
           $2->{2}:=EliminaSpazi (Substring($MyText;34;30))
           $2->{3}:=EliminaSpazi (Substring($MyText;64;30))
           $2->{4}:=EliminaSpazi (Substring($MyText;94;4))
           $2->{5}:=EliminaSpazi (Substring($MyText;98;30))
           If ((Ascii(Substring($MyText;128;1))+1)<=Size of array(<>tType))
           $2->{6}:=<>tType{Ascii(Substring($MyText;128;1))+1}
           End if
      Else
           ALERT("No MP3 Tag.")
        End if
        $2->{7}:=$1
End if

Il metodo EliminaSpazi si trova in questa faq.
Codice Codice Cambiare logo nei formati
A volte capita di modificare il logo nei formati, questo metodo magari non risolve tutte le casistiche ma ridimensiona al vertice in alto a sinistra

If (False)
  ` $1 = Puntatore alla variabile contenente il Logo
  ` $2 = Altezza del campo che si vuole ottenere
  ` $3 = Larghezza del campo che si vuole ottenere
  ` $4 = Object Name della variabile

  ` Inserire il richiamo a questo metodo nell'oggetto del form

AdeguaLogo (->var_Logo;114;0;"Logo")

  ` con evento "On printing detail" - "On display detail"
  ` Sembra che funzioni solo su Variabili
End if

C_INTEGER($AltezzaPrevistaInPixel;$LarghezzaPrevistaInPixel;$SizeOrizzontale;$SizeVerticale)

$AltezzaPrevistaInPixel:=$2
$LarghezzaPrevistaInPixel:=$3
PICTURE PROPERTIES($1->;$Larghezza;$Altezza)
Case of
: ($AltezzaPrevistaInPixel>0)
$Coefficiente:=$Altezza/$AltezzaPrevistaInPixel
: ($LarghezzaPrevistaInPixel>0)
$Coefficiente:=$Larghezza/$LarghezzaPrevistaInPixel
Else
$Coefficiente:=1
End case
$SizeOrizzontale:=($Larghezza/$Coefficiente
$SizeVerticale:=$Altezza/$Coefficiente
GET OBJECT RECT(*;$4;$left;$top;$right;$bottom)
MOVE OBJECT(*;$4;$left;$top;$SizeOrizzontale+$left;$SizeVerticale+$top;*)

Codice Codice Rendere maiuscole tutte le prime lettere
Il metodo che segue permette di rendere maiuscole la prima lettera di un campo alfa e tutte quelle di tutte le altre parole della stringa, considerando come separatore di parola lo spazio.

      `$1 è il testo a cui applicare il metodo
      `$0 il risultato
C_STRING(30;$risultato_s;$0;$vch_s)
C_LONGINT($pos_l)
$risultato_s:=$1
$risultato_s:=Uppercase(Substring($risultato_s;1;1))+Lowercase(Substring($risultato_s;2;80))
$pos_l:=Position(" ";$risultato_s)
While ($pos_l>0)
    $vch_s:=" "+Substring($risultato_s;$pos_l+1;1)
    $risultato_s:=Replace string($risultato_s;$vch_s;Uppercase($vch_s);1)
    $pos_l:=Position(" ";$risultato_s;$pos_l+1)
End while

$0:=$risultato

Novita' Novita' 4D 2004 - Spostamento automatico degli oggetti
Disegnando un form, è stato sempre possibile spostare gli oggetti di 10 punti nelle varie direzioni usando insieme alle frecce il maiuscolo.

Dalla versione 2004, fra le preferenze (in "Preferences - Design Mode - Form Editor"), è stata inserita la possibilità di modificare il numero di punti per questo tipo di spostamento ("Step using keyboard").

Novita' Novita' Customizer e 2004: dimensione della finestra di avvio
Con la versione 2004 le funzionalità del Customizer Plus sono state integrate in 4th Dimension. Una opzione che è "scomparsa" è quella riguardante la dimensione della finestra di avvio (nelle "Preferences" si può scegliere solo se mostrarla o no).
A ciò si può facilmente ovviare da linguaggio di programmazione: ad esempio, se usavamo una dimensione costante per la finestra di avvio, basta eseguire

SET WINDOW RECT(left;top;right;bottom)

nello Startup method per ottenere lo stesso risultato.

Codice Codice La compressione dei Blob
I blob (Binary Large OBjects) sono tipi di dati, quindi campi o variabili, che possono contenere fino a 2GB di informazioni.

Per gestire blob di grosse dimensioni è possibile usare il comando Compress Blob con eventualmente 2 opzioni, per scegliere fra una maggiore compressione o una maggiore velocità. Ecco i due esempi:

COMPRESS BLOB(blob_blb;Compact compression mode)
COMPRESS BLOB(blob_blb;Fast compression mode)

Il comando per riportare il blob alle dimensioni originali è il seguente:
EXPAND BLOB(blob_blb)

Attenzione: se il blob contiene meno di 255 caratteri la compressione non ha luogo. Questo vuol dire che il successivo EXPAND BLOB potrebbe dare un errore. La modalità corretta di usarlo per evitare problemi è la seguente:

BLOB PROPERTIES(blob_blb;$compressione_l;$dim_espanso_l;$dim_corrente_l)
If ($compressione_l#Is not compressed)
  EXPAND BLOB(blob_blb)
End if
Tecniche Tecniche Entry filter per i dati di tipo orario AM/PM
Ecco un entry filter interessante, da usare per i valori di tipo time inglesi, cioè con la sigla AM/PM in fondo:

!0&"0;1"#&9#:&"0-5"#&9# ~"A;P"#M


Codice Codice Importare numeri negativi da Excel o FileMaker
Il comando Num di 4D non riconosce come negativi i numeri passati racchiusi tra parentesi, mentre alcuni programmi come FileMaker o Excel possono utilizzare questo formato per l'esportazione di tali numeri, senza dunque la presenza del segno "-".
Ecco dunque un frammento di codice che consente di ottenere il numero corretto a partire da un testo passato come parametro:

C_TEXT($1;$tNumText_t)
C_REAL($0)
C_LONGINT($iLen:l)
$tNumText_t:=$1
$iLen_l:=Length($tNumText_t)

If ($tNumText_t[[1]]=Char(40)) & ($tNumText_t[[$iLen_l]]=Char(41))
        ` il numero è negativo
    $tNumText_t:=Replace string($tNumText_t;"(";"-")
    $tNumText_t:=Replace string($tNumText_t;")";"")
End if

$0:=Num($tNumText_t)

Info Info Falso bug della tastiera sulla 2004
Molti sviluppatori hanno segnalato un baco della versione 2004 riguardante la tastiera su Windows: in certi casi i caratteri mostrati usando Shift+numero risultavano non coincidenti al risultato desiderato.

Il problema in realtà non esiste, in quanto si presenta solo perché la combinazione dei tasti che permette di passare in modalità User (Alt+Shift+Clic, per altre info vedere questa faq) è la stessa utilizzata dal sistema operativo per modificare in automatico, nei Servizi di testo e lingue di input, la lingua di keyboard input.

Basta dunque rimuovere le altre lingue o modificare/cancellare la combinazione di tasti di Windows per risolvere il "baco".

Info Info Operatori binari: esempi
Vediamo qualche esempio di operatori binari, a partire dalla faq che ne descrive l'uso:

Quando si esegue un AND fra bit il risultato segue queste regole:
  1 & 1 --> 1
  0 & 1 --> 0
  1 & 0 --> 0
  0 & 0 --> 0

cioè, il risultato è 1 solo se entrabi i bit valgono 1.

Esempio: 0x0000FFFF & 0xFF00FF00 è uguale a 0x0000FF00


L'OR (inclusive) usa la seguente tabella:
  1 | 1 --> 1
  0 | 1 --> 1
  1 | 0 --> 1
  0 | 0 --> 0

cioè, il risultato è 1 se uno dei bit vale 1.

Esempio: 0x0000FFFF | 0xFF00FF00 è uguale a 0xFF00FFFF.


L'OR esclusivo invece si comporta così:
  1 ^| 1 --> 0
  0 ^| 1 --> 1
  1 ^| 0 --> 1
  0 ^| 0 --> 0

cioè, il risultato è 1 solo se UNO SOLO dei bit confrontati vale 1.

Esempio: 0x0000FFFF ^| 0xFF00FF00 restituisce 0xFF0000FF


Lo Shift a sinistra/destra sposta i bit verso sinistra/destra di un numero N di posti. I bit mancanti vengono rimpiazzati da zeri. Ciò significa che se il numero di bit da shiftare passato è maggiore di 31 il risultato dello shift sarà sempre 0x00000000 poiché tutti i bit vengono persi.
Lo Shift a sinistra/destra corrisponde a moltiplicare/dividere il numero passato per 2^N (con N numero di posti dello shift)

Esempi:
Bit Shift a sx: 0x0000FFFF << 8 restituisce 0x00FFFF00
Bit Shift a dx: 0x0000FFFF >> 8 restituisce 0x000000FF


Altri esempi sui bit operators:
Bit Set: 0x00000000 ?+ 16 restituisce 0x00010000
Bit Clear: 0x00010000 ?- 16 restituisce 0x00000000
Bit Test: 0x00010000 ?? 16 restituisce True

Plugin Plugin Connessione ODBC con i comandi della 2004
A differenza delle versioni precedenti, in cui era necessario l'uso del plugin apposito per connettersi a database ODBC, con la versione 2004 del tool i comandi basilari per la connessione a data source esterni sono direttamente disponibili nel linguaggio di programmazione.

I comandi ODBC LOGIN e ODBC LOGOUT permettono di accedere (e di disconnettersi) direttamente alle fonti di dati esterne, passando al primo comando solo nome dell'origine dati, user e password.

Se, come con le versioni precedenti, fosse necessario ottenere un ID di connessione (ad esempio per connettersi contemporaneamente a più di una origine dati), bisogna allora utilizzare il plugin 4D ODBC Pro, che opzione a pagamento.
Comandi Comandi Operatori Binari
Nei casi in cui è necessario lavorare a livello binario, cioè dei Bit, 4d fornisce una serie di operatori che lavorano con numeri interi o longint.

AND = & = Numero & Numero
OR = | = Numero | Numero
XOR = ^| = Numero ^| Numero

Per spostare il numero binario verso sinistra o verso destra di un numero di posti:

Left Bit Shift = << = Numero << diQuantiBit
Right Bit Shift = >> = Numero >> diQuantiBit

Per controllare e modificare un singolo bit all'interno di un numero

Attiva Bit = ?+ = Numero ?? qualeBit
Azzera Bit = ?- = Numero ?? qualeBit
Controlla Bit = ?? = Numero ?? qualeBit
Novita' Novita' Generare il metodo che crea un Report
Nell'ultima pagina del wizard del Quick Report della 2004 l'ultimo pulsante di opzione, Build 4D Code permette di generare il codice corrispondente al report appena definito e di incollarlo negli Appunti.

Questa funzione è molto utile perché permette di modificare via linguaggio un report

Build 4D Code è disponibile solo in ambiente User

Tecniche Tecniche Stampare un campo testo in variable frame
Per stampare un campo testo in modalità "Variable frame" bisogna utilizzare alcuni accorgimenti.
Intanto la variabile o il campo debbono essere alti almeno due righe (basta ridimensionare verso il basso la variabile) e usare questo piccolo frammento di codice:

Case of
    : (Form event=On Printing Detail)
        If ([Tabella]CampoTesto="")
           [Tabella]CampoTesto:=" " `senza salvare!
        End if
End case

oppure

Case of
    : (Form event=On Printing Detail)
        vTesto_T:=[Tabella]CampoTesto
End case

Plugin Plugin Impostare l'handshake della porta seriale
Usando i comandi per la gestione della seriale su 4D è possibile scegliere fra le varie opzioni il protocollo Hardware: questo imposta sia il DTR che il CTS.

Potrebbe esserci qualche raro caso in cui la seriale si aspetta solo il DTR attivo e non il CTS: in questo caso si può usare plug-in Serial Toolkit Pro, distribuito da Deep Sky Technologies.

La funzione STK_HShake permette di impostare il metodo di handshake con queste opzioni:
err := STK_HShake(refNum; handShakeType; xOnChar; xOffChar)

dove il valore di handShakeType può essere:

0 = no handshake
1 = Xon/Xoff
2 = CTS
3 = DTR
4 = CTS/DTR

Codice Codice Calcolo del checksum per il protocollo MODBUS
Ecco un metodo utilizzabile per calcolare il CRC per il protocollo industriale MODBUS.

Il CRC è un valore di 16 bit calcolato in base a tutti i byte che compongono il pacchetto (il parametro che riceve il metodo), a partire dal byte alto ed esclusi i byte del CRC. Se il CRC non è corretto la macchina slave ignora il pacchetto.

La procedura usa due array, arrHi e arrLow, dove sono memorizzati i valori necessari al calcolo del CRC.
Viene inoltre utilizzato il metodo u_EsadecimaleToDec il cui testo si trova nella faq Conversione da esadecimale a decimale.


$reg_Hi:=0x00FF
$reg_Low:=0x00FF
$test:=$1
C_STRING(80;$1;$0)
For ($i;1;Length($test);2)
  C_LONGINT($index)
  $deci:=u_EsadecimaleToDec ($test[[$i]]+$test[[$i+1]])
  $index:=$reg_Hi ^| $deci
  $reg_Hi:=$reg_Low ^| arrHi{$index+1}
  $reg_Low:=arrLow{$index+1}
End for
$risultatoA:=$reg_Hi << 8
$risultato:=$risultatoA ^| $reg_Low
$0:=Substring(String($risultato;"&x");3)


Il metodo di calcolo è utilizzabile anche per il calcolo del CRC del protocollo J-Bus.

Codice Codice Come controllare il il numero di licenze dei Plug-in di 4D Server
(da un'idea di Massimo Giannessi)

Il problema si pone quando si installa un numero inferiore di licenze di Plug-in rispetto al numero di licenze 4D Client

con l'istruzione ' Is license available(4d view license) ' ritorna vero se ho almeno una licenza, ma non mi dice quante licenze sono già impegnate dai vari 4D client

` Procedura in StartUp
vErrView:=False
ON ERR CALL("ErrView")
QLV_StartUp
ON ERR CALL("")

` Procedura ErrView
vErrView:=True

` Procedura QLV_StartUp
`procedura che iniziallizza i component Italsoftware con plug-in 4D View

La variabile flag vErrView posso utilizzarla prima di fare le chiamate alle procedure che utilizzano i Plug-in (in questo caso di 4D View)


Plugin Plugin Collegarsi ad un SQL Server via ADO
Nella 2004, 4D presenta un nuovo plugin "4D for ADO" con cui è possibile dialogare e scambiare dati con la maggior parte dei database relazionali sui sistemi Windows.
Ecco un esempio che mostra il collegamento ad un database utilizzando il nuovo plugin ADO attraverso ODBC.
C_TEXT($ConnStr)
C_LONGINT($ConnID_l)

` --- ADO usando ODBC---
$ConnStr:="Driver={nome_driver};"
$ConnStr:=$ConnStr+"Server=server_DNS;"
$ConnStr:=$ConnStr+"Database=nome_database;"
$ConnStr:=$ConnStr+"Uid=utente;"
$ConnStr:=$ConnStr+"Pwd=password"

` Mi collego al database identificato nella stringa preparata come sopra
$ConnID_l:=DBGateway_Connect ("server_DNS";$ConnStr)

`Comando di chiusura della connessione
DBGateway_Close ($ConnID_l)

Comandi Comandi Creare una selezione da valori multipli
Nel caso in cui si abbia la necessità di fare un ricerca per valori multipli su un campo indicizzato, piuttosto che accodare diverse query, è meglio usare QUERY WITH ARRAY che crea la selezione di record in modo molto più veloce.

Ecco un esempio, per selezionare tutti i clienti di Milano, Roma, Napoli:

ARRAY STRING (2;Province_at;2)
Province_at{1}:="MI"
Province_at{2}:="RM"
Province_at{3}:="NA"
QUERY WITH ARRAY ([Clienti]Provincia; Province_at)

Nota: non è necessario passare la tabella perchè il comando cambia la selezione relativa al campo indicizzato passato come primo parametro.
Info Info Le impostazioni per gli oggetti nelle maschere
Il form editor della 2004 presenta molte funzioni interessanti; eccone una molto utile quando si lavora su un database disegnando o modificando molte maschere.

Quando si usa un oggetto esso presenta alcune impostazioni predefinite, come ad esempio lo stile del testo negli oggetti Label. Per cambiare queste impostazioni è possibile usare il comando "Use as Template" nel menu contestuale (control clic su Mac o tasto destro su Windows) che appare sull'oggetto che è stato definito a proprio piacere.
Novizi Novizi I tre ambienti di 4D
4D può lavorare in te modalità differenti:

- Design: è quella usata dal programmatore per sviluppare il database, dove si può accedere alla struttura delle tabelle, all'editor dei metodi, delle maschere, etc;

- User: è la modalità di inserimento diretto dei dati nelle tabelle; cioè in pratica è possibile navigare con gli strumenti standard di 4d all'interno di tutto il database; utilizzato in genere solo dal programmatore e dall'amministratore di sistema.

- Custom: è la modalità visibile all'utente finale (per capirci, quella che si vede utilizzando semplicemente 4D Runtime); l'utilizzo del programma è deciso dal programmatore con i menu personalizzati.

Per scegliere la modalità con cui si apre una applicazione, dal Design basta andare nel menu Edit-Preferences (o Mela-Preferences) e selezionare l'ambiente desiderato.
Comandi Comandi Cercare un elemento in un indice: Find Index Key
Con il comando Find Index Key(campo_indicizzato;termine) è possibile cercare direttamente una voce all'interno di un indice, senza toccare la selezione corrente.

Ad esempio, è estremamente utile dall'interno di una maschera d'inserimento per controllare che una voce non sia già presente nella base dati, senza perdere il record corrente.

La funzione ritorna -1 se la voce non è nell'indice, altrimenti se la trova ritorna il numero di record corrispondente.

Da notare che il termine da cercare deve essere necessariamente una variabile perché il comando la riempie con il valore trovato: in questo modo si possono fare le ricerche sui campi Alpha usando la "@".
Comandi Comandi Controllo Ortografico Integrato
Il comando SPELL CHECKING avvia il controllo della correttezza dei termini usati nel campo o variabile di tipo testo in cui si trova il cursore nella maschera corrente.

Se il termine non viene trovato appare la relativa finestra di dialogo dove è possibile correggere, ignorare o aggiungere il termine nel proprio dizionario personale.

4th Dimension usa il dizionario corrente, corrispondente alla lingua dell'applicativo usato; quindi normalmente in Italia avremo preimpostato l'Inglese.

Con il comando SET DICTIONARY è possibile scegliere fra Inglese, Francese, Spagnolo e Tedesco con una serie di loro varianti.

Al momento (settembre 2005) l'Italiano non è disponibile.
Comandi Comandi Linee a colori alternati nelle liste di record
Il comando Displayed line number è uilizzabile solo all'interno dell'evento On Display Detail; ecco un esempio di utilizzo per colorare un campo in modo diverso in funzione della riga selezionata.

If (Form event=On Display Detail)
  If (Displayed line number % 2 = 0)
      `Nero su bianco per le righe pari
      SET RGB COLORS([Tabella]Campo; -1; 0x00FFFFFF)
  Else
      `Nero su celeste per righe pari
      SET RGB COLORS([Tabella]Campo; -1; 0x00E0E0FF)
    End if
End if
Info Info Creare la finestra di Informazioni (About)
Con il comando SET ABOUT puoi specificare il nome del comando da menu e il corrispondente metodo da eseguire per la finestra di Informazioni standard sull'applicazione corrente.

Nel metodo si può usare il comando DIALOG; 4D aggiungerà in cima alla finestra altre informazioni come l'icona di 4D, il numero di versione di 4D e del compilatore.

Per non mostrare queste informazioni aggiuntive, basta mostrare la finestra di dialogo in un processo a parte. Se hai già il metodo che mostra la Dialog, basta aggiungere queste righe di codice:

Case of
  : (Count parameters=0)
    $Processo_l:=New process(Current method name;Maxint;Current method name;"ProcessoAParte")
  Else
    
    `Codice Originale
    $w:=Open Form Window([Tabella];"MiaAbout")
    DIALOG([Tabella];"MiaAbout")
    CLOSE WINDOW

End case

Info Info Modificare le preferenze Database da Runtime
Con la versione 2004 è possibile modificare le preferenze di Database anche dall'ambiente Runtime: è possibile creare un elemento di menu e scegliere come azione automatica associata (Associated Standard action) "Preferences".

Da notare che i comandi da menu associati alle azioni Quit e Preferences sono automaticamente spostati nelle posizioni standard per il sistema operativo in cui ci si trova.
Codice Codice Convertire un metodo per eseguirlo su Server
Alcune procedure eseguite su Client sarebbero eseguite in maniera molto più veloce su Server, ad esempio se si modificano tanti record e non ci sono legami all'interfaccia. Ora supponiamo di avere una procedura fatta così:

  `MioMetodo
  C_REAL($0;Tot_l)
  Tot_l:=0
  ALL RECORDS([Tabella])
  APPLY TO SELECTION([Tabella];Tot_l:=Tot_l+([Tabella]Prezzo*[Tabella]Quantità))
  $0:=Tot_l
  
Per evitare di modificare tutte le procedure che chiamano MioMetodo, interveniamo sullo stesso premettendo una chiamata a se stessa come stored procedure aggiungendo un parametro; poi aspettiamo il risultato dal server e lo ritorniamo come valore, mantenendo così invariato il funzionamento di MioMetodo dal punto di vista delle altre procedure.

  `MioMetodo
  C_REAL($0;Tot_l)
  C_TEXT($1;$Parametro_t)
  C_LONGINT($Processo_l)
  C_BOOLEAN(Pronto_b;HoLetto_b)
  
Case of
  : (Count parameters=0)
    $Processo_l:=Execute on server(Current method name;Maxint;Current method name;"SulServer")
    
    `Questo ciclo aspetta che la Stored Procedure sia Pronta a darmi il valore
    Pronto_b:=False
    Repeat
      DELAY PROCESS(Current process;60)
      IDLE
      GET PROCESS VARIABLE($Processo_l;Pronto_b;Pronto_b)
    Until (Pronto_b)
    GET PROCESS VARIABLE($I_Process;Tot_l;Tot_l)
    
    `E poi avverto la Stored Procedure che ho letto, così si chiude
    HoLetto_b:=True
    SET PROCESS VARIABLE($I_Process;HoLetto_b;HoLetto_b)
    
    `Ritorno il valore
    $0:=Tot_l

  Else
    `Questa parte viene eseguita sul server come Stored Procedure
    Pronto_b:=False
    
    `CODICE ORIGINALE
    Tot_l:=0
    ALL RECORDS([Tabella])
    APPLY TO SELECTION([Tabella];Tot_l:=Tot_l+([Tabella]Prezzo*[Tabella]Quantità))
    
    `avverto che il risultato è pronto
    Pronto_b:=True
    `aspetto che il client abbia letto e poi chiudo
    HoLetto_b:=False
    Repeat
      DELAY PROCESS(Current process;3)
      IDLE
    Until (HoLetto_b)
End case

Novizi Novizi Le estensioni dei file 4D
Ecco un elenco delle estensioni dei vari tipi di file usati da 4th Dimension:

- .4db : la struttra del database;
- .rsr : risorse della struttura, quando si sposta una struttura bisogana spostare sia il file .4db che il file .rsr;
- .4dd : file dei dati, è indipendente dalla struttura;
- .4dr : file di risorse del file di dati, da spostare sempre insieme al .4dd;
- .4dc : database compilato;
- .4df : ricerca salvata (nel query editor);
- .4dl : file log;
- .4ds : data segment, i file dati "aggiuntivi" al file dati principale;
- .4dx : external package;
- .4fi : filtro ASCII;
- .4fr : formula;
- .4lb : etichette salvate nel label editor;
- .4qr : quick report salvati;
- .4st : set salvati;
- .4ug : utenti e gruppi;
- .4vr : varialbili;
- .tfr: formule (salvate ad esempio nel quick report o nell'order by);

Su Mac le risorse sono inserite nei file struttura e dati, la struttura non ha quindi estensione, mentre il file dati ha estensione ".data".

Tecniche Tecniche Disabilitare il Range checking solo in una parte di codice
Compilando con l'opzione Range Checking 4d verifica ogni singolo riferimento o impostazione di valore delle variabili e dei campi. E' ad esempio utile quando si tenta di usare/assegnare un elemento di un array oltre la dimensione dichiarata (il quinto elemento di un array di size 4, ad esempio): in questo caso senza range checking il programma compilato potrebbe dare errori di qualsiasi tipo, anche chiudersi inaspettatamente (perchè accede ad una zona di memoria protetta). Con il range checking attivo 4d intercetta l'errore e avverte l'utente.

Questo pero' potrebbe rallentare in modo significativo un programma compilato in punti dove ci sono molti calcoli e cicli annidati; in questi casi è possibile disabilitare per quella parte di codice il Range Checking e riabilitarlo subito dopo con queste istruzioni:

  `%R-
... qui va il codice che può girare senza range checking, perchè ben controllato e sicuro
  `%R+

Quindi: il primo %R- disabilita il Range checking, il secondo %R+ lo riabilita.
Tecniche Tecniche Disabilitare alcuni Warning in compilazione
Quando si effettua una compilazione del codice, alcune condizioni particolari potrebbero essere mostrate al programmatore come Warning.

Per disabilitare all'interno di un certo codice un determinato tipo di Warning è sufficiente aggiungere una riga di commento con questa sintassi

`%W-(numero del warning)

Ad esempio, per disabilitare questi Warning
1: Pointer in an array declaration (518.5)

si può scrivere, magari in uno dei metodi chiamati COMPILER_xxx (che vengono compilati per primi):

  `%W-518.5
Codice Codice Lanciare un'applicazione esterna
Dalla versione 2004 il comando LAUNCH EXTERNAL PROCESS permette di eseguire applicativi esterni a 4D.
Nelle versioni precedenti si può usare il comando AP Sublaunch del plugin gratuito 4D Pack.

Ad esempio, è possibile cambiare i privilegi di accesso di un file usando il comando da Terminale su Mac:
LAUNCH EXTERNAL PROCESS ("chmod +x /cartella/documento")

Su Windows è possibile nascondere la finestra della Console DOS usando anche il comando SET ENVIRONMENT VARIABLE.

Ecco un esempio:
SET ENVIRONMENT VARIABLE("_4D_OPTION_CURRENT_DIRECTORY";"C:\\4D")
SET ENVIRONMENT VARIABLE("_4D_OPTION_HIDE_CONSOLE";"true")
LAUNCH EXTERNAL PROCESS("mycommand")
Codice Codice Evidenziare un elemento al passaggio del Mouse
Ecco un esempio di uso di un paio di nuovi eventi introdotti nella versione 4D 2004.

Basta incollare questo codice nel metodo di un oggetto (di tipo testuale): quando ci si passa sopra con il mouse il testo contenuto viene evidenziato, un po' come quando con il browser si passa su un link.

Case of
  : (Form event=On Mouse Enter )
    FONT STYLE(Self->;Underline )

  : (Form event=On Mouse Leave )
    FONT STYLE(Self->;Plain )
End case

Codice Codice I Parametri e la funzione Massimo
In 4D è possibile chiamare un metodo passando un certo numero di parametri non necessariamente prefissato.

I parametri all'interno del metodo vengono usati direttamente con la sintassi $1, $2, $3, etc o indirettamente con la sintassi ${num}: il metodo può sapere quanti parametri sono stati passati usando la funzion e Count parameter.
Il tipo dei parametri può essere comunque dichiarato usando la sintassi C_XXX(${n}) dove n indica che da quel parametro in poi sono tutti di tipo C_XXX.

Ecco un esempio di funzione che sfrutta queste caratteristiche:

` Metodo Massimo
` Massimo ( Valore { ; Valore2... ; ValoreN } ) -> ritorna il numero massimo
C_REAL ($0;${1}) ` Tutti i parametri sono dichiarati numeri Reali
$0:=${1}
For ($param_l;2;Count parameters)
  If (${$param_l}>$0)
    $0:=${$param_l}
  End if
End for

Quindi sono valide tutte le seguenti chiamate:
maggiore:= Massimo (numero1;numero2)
maggiore:= Massimo (n1;n2;n3;n4;n5;n6;n7;n8;n9;n10;n11;n12;n13;n14;n15;n16)
maggiore:= Massimo ((current date-$datanascita_1)/365,25;(current date-$datanascita_2)/365,25)

Codice Codice Metodo di controllo dell'Input da tastiera
Usando l'evento On Before Keystroke si riesce ad intercettare cosa viene scritto nella corrente area di testo (dove si trova il cursore), prima che sia accettato come nuovo valore del campo o della variabile corrispondente.
Il metodo Handle keystroke utilizza una seconda variabile utilizzabile per gestire quello che si sta inserendo.
I parametri sono il puntatore all'area e il puntatore ad una variabile secondaria.
Il metodo ritorna il nuovo valore dell'area nella variabile e ritorna Vero se il testo è cambiato.

    ` Handle keystroke
    ` Handle keystroke ( Pointer ; Pointer ) -> Boolean
    ` Handle keystroke ( -> areaIns ; -> valoreCorrente ) -> E' un valore nuovo

C_POINTER ($1;$2)
C_TEXT ($nuovoValore_t)

GET HIGHLIGHT ($1->;$inizio_l;$fine_l) ` Prendi l'intervallo selezionato nell'area
$nuovoValore_t:=$2->` Inizia a lavorare con il valore corrente
    
Case of ` Controlla il tasto premuto
    ` E' stato premuto il tasto Backspace (Delete)
  : (Ascii (Keystroke)=Backspace )
    ` Cancella i caratteri selezionati o il carattere a sinistra
    $nuovoValore_t:=Substring ($nuovoValore_t;1;$inizio_l-1-Num($inizio_l=$fine_l))+Substring($nuovoValore_t;$fine_l)

    ` E' stato premuto un carattere accettato
  : (Position (Keystroke;"abcdefghjiklmnopqrstuvwxyz -0123456789")>0)
    If ($inizio_l#$fine_l)
      ` Se c'è una seleziona il carattere la sostituisce tutta
      $nuovoValore_t:=Substring($nuovoValore_t;1;$inizio_l-1)+Keystroke+Substring($nuovoValore_t;$fine_l)
    Else
      ` Non c'è selezione, solo il cursore...
        Case of
          ` .. all'inizio
          : ($inizio_l<=1)
           $nuovoValore_t:=Keystroke+$nuovoValore_t
           ` ... alla fine
          : ($inizio_l>=Length($nuovoValore_t))
           $nuovoValore_t:=$nuovoValore_t+Keystroke
          Else
           ` ... in mezzo al testo
           $nuovoValore_t:=Substring($nuovoValore_t;1;$inizio_l-1)+Keystroke+Substring($nuovoValore_t;$inizio_l)
          End case
        End if

        ` E' stata premuta una Freccia, accetta comunque il tasto
      : (Ascii(Keystroke)=Left Arrow Key )
      : (Ascii(Keystroke)=Right Arrow Key )
      : (Ascii(Keystroke)=Up Arrow Key )
      : (Ascii(Keystroke)=Down Arrow Key )
`
Else
    ` E' un carattere non accettato, lo filtra del tutto
  FILTER KEYSTROKE ("")
End case
    ` Il valore è cambiato?
$0:=($nuovoValore_t#$2->)
    ` Ritorna comunque il valore
$2->:=$nuovoValore_t

Clic qui per un esempio di utilizzo del metodo, per mascherare l'inserimento delle password
Plugin Plugin 4D Open e i Network Component
Dalla versione di 4D 2003, i componenti non-TCP/IP non sono più usati, per cui il comando OP Load Network Component è obsoleto.

L'ID del TCP/IP è 29 su Mac OS e 2 su Windows. Quindi quando viene richiesto un componente in un comando di 4D, bisogna usare direttamente la costante, come nell'esempio:

PLATFORM PROPERTIES($sistema_l)
If($sistema_l<3)
  netCompID:=29 `sono su Mac
Else
  netCompID:=2 `sono su Windows
End if
$error:=OP Select 4D Server (netCompID;$NomeServer;$IDServer;True)

1
Comandi Comandi Delay Process e il processo User/Custom Menus
Il comando Delay process ha una particolarità: non funziona nel processo "User/Custom Menus", cioé non lo rallenta.

Questo è normamente causa di confusione quando una procedura che contiene un Delay Process viene testata in ambiente User: la soluzione è di ricordarsi di selezionare il pulsante "New Process" nella finestra di Execute Method (in modo da lanciare la procedura in un processo separato).

Oppure è possibile usare un metodo alternativo:

    `Metodo Pausa
    `Aspetta 2 secondi o il numero di secondi che gli passi come parametro
    `funziona anche in User/Runtime
C_LONGINT($sec_l)
C_TIME($inizio_h)
If (Count parameters>0)
  $sec_l:=$1
Else
  $sec_l:=2
End if
$inizio_h:=Current time
While (Abs(Current time-$inizio_h)<$sec_l)
  DELAY PROCESS(Current process;30)
End while

Plugin Plugin I parametri di OC Set login option e OC Get login option
In 4d Odbc 2003 i comandi OC Set login option e OC Get login option permettono di impostare o leggere i parametri di una connessione ad un database ODBC-compatibile. I parametri a disposizione, con il relativo valore della costante, sono:

SQL_ACCESS_MODE 101
SQL_AUTOCOMMIT 102
SQL_LOGIN_TIMEOUT 103
SQL_OPT_TRACE 104
SQL_OPT_TRACEFILE 105
SQL_TRANSLATE_DLL 106
SQL_TRANSLATE_OPTION 107
SQL_TXN_ISOLATION 108
SQL_CURRENT_QUALIFIER 109
SQL_ODBC_CURSORS 110
SQL_QUIET_MODE 111
SQL_PACKET_SIZE 112

Vediamo alcuni dei valori che impostabili/leggibili:

SQL_ACCESS_MODE
0 = Read/Write
1 = Read Only
Imposta il tipo di accesso (lettura scrittura/sola lettura)

SQL_LOGIN_TIMEOUT
Integer
tempo per la disconnessione: se posto a 0 il timeout viene disabilitato

SQL_OPT_TRACE
0 = Trace off
1 = Trace on
abilita/disabilita il trace delle operazioni su un file di log

SQL_OPT_TRACEFILE
File name
Se SQL_OPT_TRACE è 1, il log verrà scritto su questo file (il file di default è SQL.LOG)

SQL_QUERY_TIMEOUT
Integer
0=No timeout
E' il tempo di timeout di esecuzione di una query prima che venga cancellata

Tecniche Tecniche Lanciare 4d 6.8 o 4d 2003 come root
Su Mac OS X per accedere alla porta 80 (come web server) o alla 443 (come web con SSL) occorre lanciare l'applicazione 4D da utente root (per le versioni 6.8 e 2003, la 2004 lo fa da sola)

Ecco il comando da eseguire:
sudo -b /System/Library/Frameworks/Carbon.framework/Versions/Current/Support/LaunchCFMApp

Ma in realtà il file da lanciare non è il package esterno, ma il file dell'applicativo vero e proprio all'interno; puoi usare il comando "Mostra Contenuto Pacchetto" su un'applicazione Mac OS X per esplorarne il contenuto.

Quindi il comando sopra sarà, posto di avere 4D 6.8 nella cartella Applicazioni:

sudo -b /System/Library/Frameworks/Carbon.framework/Versions/Current/Support/LaunchCFMApp /Applications/4th\ Dimension/Contents/MacOS/4th\ Dimension

Oppure nel caso di 4D 2003 sempre nella cartella Applicazioni:

sudo -b /System/Library/Frameworks/Carbon.framework/Versions/Current/Support/LaunchCFMApp /Applications/4th\ Dimension/Contents/MacOSClassic/4th\ Dimension

Un suggerimento: non state a scrivere il pathname a mano, ma con la finestra del Terminale aperta potete trascinare dentro un file qualsiasi e il pathname verrà compilato completo di tutti i caratteri ben formattati. Ecco ad esempio il pathname della mia personale posizione di 4D 2003, il cui percorso è 4D:4D 2003.6:4th Dimension 2003.6

/4D/4D\ 2003.6/4th\ Dimension\ 2003.6.app/Contents/MacOSClassic/4th\ Dimension
Tecniche Tecniche Scelta della chiave primaria
Un punto critico nell'ideare lo schema concettuale di un database è la scelta degli attributi caratteristici dei set di entità che sono in definitiva le tabelle. Un attributo o un insieme di attributi i cui valori identifichino in modo univoco ogni entità del set (record) è chiamato chiave.

In linea di principio ogni tabella dovrebbe avere una chiave, in modo che ogni record sia distinguibile dall'altro.

Anche se il mondo reale offre a volte delle chiavi naturali, in molti casi è preferibile usare un dato a parte che non possa cambiare per nessun motivo: i programmatori 4D hanno a disposizione per tale fine la funzione Sequence number che genera un numero univoco per ogni record creato per ogni tabella.

D'altro canto l'uso di Sequence number ha degli svantaggi da considerare,
dovuti proprio alla propria unicità e al legame diretto con la struttura.
Per esempio:
- in caso di danneggiamento del file dati, quando l'unica soluzione è
esportare i dati e ricrearli in un nuovo file;
- in caso di esportazione/importazione di dati per il passaggio ad una
nuova versione (vedi la faq Rapido Trasferimento Dati);
- unione dei dati provenienti da strutture distinte, anche se uguali.

Per ovviare a questi inconvenienti è meglio usare dei contatori creati in
proprio. Ecco un esempio in cui la tabella [MieiContatori] ha un campo per
ogni contatore che voglio tenere:

C_POINTER($1;$PuntaAlCampo)
C_LONGINT($0)

$PuntaAlCampo:= $1

READ WRITE([MieiContatori])

If (Records in table([MieiContatori])=0)
        CREATE RECORD([MieiContatori])
        SAVE RECORD([MieiContatori])
End if

ALL RECORDS([MieiContatori])

While (Locked([MieiContatori])) ` controllo se è bloccato
        DELAY PROCESS(Current process;10)
        LOAD RECORD([MieiContatori])
End while

$PuntaAlCampo-> := $PuntaAlCampo->+1
$0 := $PuntaAlCampo->
SAVE RECORD([MieiContatori])
UNLOAD RECORD([MieiContatori])
READ ONLY([MieiContatori])


2
Info Info Dove sono registrati gli ultimi server
Quando si apre il 4D Client è possibile scegliere fra un elenco un degli ultimi server a cui si è fatto accesso. Questi server vengono registrati in un file che è possibile copare o eliminare da ogni singola macchina client (ad esempio eliminando quelli non più disponibili se diventano troppi).

Nella versione 2003 e 6.8 questa cartella si trovava nella cartella 4D principale, mentre nella 2004 si trova nella cartella 4D dell'utente corrente.

Windows
C:\documents and settings\(nome utente)\Application Data\4D\Favorites 2004

Mac
MacHD:Users/(nome utente)/Library/Application/4D/Favorites 2004


Macro Macro Una macro per il form di output
Ecco il testo di una macro da utilizzare per riempire in automatico un form di output o una lista:



Case of
:(Form event=On Load)

:((Form event=On Display Detail)|(Form event=On Printing Detail))

:((Form event=On Printing Break)|(Form event=On Printing Footer))

End case



Macro Macro Una macro per il form di input
Ecco il testo di una macro da utilizzare per creare in automatico gli eventi più usati da utilizzare nei form di input:



Case of
:(Form event=On Load)

:(Form event=On Data Change)

:(Form event=on Validate)

:(Form event=On Unload)

End case



Codice Codice Backup della cartella MAC4DX ed i file .bundle
Ciao a tutti.

Voglio allegare nel backup del database anche la cartella MAC4DX
La cartella può contenere 2 tipi di plugin :
- come files con suffisso .4CX
- come pacchetti con suffisso .bundle

Il comando 'Select folder' ed il successivo comando 'DOCUMENT LIST' non permettono di riconoscere nella lista dei documenti contenuti i file con suffisso .bundle, che si comportano in realtà come delle cartelle. E' necessario per cui introdurre anche un successivo controllo sulle cartelle contenute nella cartella MAC4DX, con il comando 'FOLDER LIST'.

Per implementare il backup dell'intera cartella MAC4DX da un bottone 'Seleziona Cartella' inserito nella finestra di impostazioni del Backup è possibile utilizzare le seguenti 3 procedure:

Nel Bottone 'Seleziona Cartella':-------------------------------------------

C_TEXT($tPath)
ARRAY TEXT($aDocuments;0)

$tPath:=Select folder("Scegli una Cartella da allegare")

If ($tPath#"")
  DOCUMENT LIST($tPath;$aDocuments)
  FOLDER LIST($tPath;$aDirectories)
  For ($i;1;Size of array($aDocuments))
    If (Find in array(IncludesFiles;$tPath+BKP_GetDirGlyph +$aDocuments{$i})=-1)
      INSERT ELEMENT(IncludesFiles;Size of array(IncludesFiles)+1)
      IncludesFiles{Size of array(IncludesFiles)}:=$tPath+BKP_GetDirGlyph +$aDocuments{$i}
    End if
  End for

  If (Size of array($aDirectories)#0)
    For ($i;1;Size of array($aDirectories))
      BKP_XML_GetBundle ($tPath+BKP_GetDirGlyph +$aDirectories{$i})
    End for
  End if

End if

  ` Method: BKP_XML_GetBundle-------------------------------------------
$tPath:=$1
DOCUMENT LIST($tPath;$aDocuments)
FOLDER LIST($tPath;$aDirectories)
For ($i;1;Size of array($aDocuments))
  If (Find in array(IncludesFiles;$tPath+BKP_GetDirGlyph +$aDocuments{$i})=-1)
    INSERT ELEMENT(IncludesFiles;Size of array(IncludesFiles)+1)
    IncludesFiles{Size of array(IncludesFiles)}:=$tPath+BKP_GetDirGlyph +$aDocuments{$i}
  End if
End for

If (Size of array($aDirectories)#0)
  For ($i;1;Size of array($aDirectories))
    BKP_XML_GetBundle ($tPath+BKP_GetDirGlyph +$aDirectories{$i})
  End for
End if

  ` Method: BKP_GetDirGlyph-------------------------------------------
C_LONGINT($platform)
C_TEXT($0)

PLATFORM PROPERTIES($platform)
If ($platform=3)
  $0:="/"
Else
  $0:=":"
End if

(PROVATO SOLO SU MAC)
Tecniche Tecniche Ricezione dati via porta seriale
I due comandi che permettono di leggere dati via porta seriale sono RECEIVE PACKET e RECEIVE BUFFER.
La differenza sostanziale sta nel fatto che RECEIVE PACKET legge sempre lo stesso numero di caratteri o fino ad un certo carattere di stop, ma il tutto con un limite di 32.000 caratteri, mentre RECEIVE BUFFER legge fino a 10Kb di buffer: quindi è necessario svuotare continuamente il buffer con una serie di comandi del tipo:

While (IP_Listen_Serial_Port)
           RECEIVE BUFFER($vtBuffer)
           If ((Length($vtBuffer)+Length(vtBuffer))>MAXTEXTLEN)
           vtBuffer:=""
           End if
           vtBuffer:=vtBuffer+$Buffer
End while

Info Info Gli oggetti cliccabili
Gli oggetti cliccabili, quelli cioè su cui è possibile utilizzare gli eventi On Clicked e On Double Clicked, sono:

- Campi e variabili di tipo boolean
- Button, default button, radio button, check box, button grid
- 3D Button, 3D radio button, 3D check box
- Pop-up menu, hierarchical pop-up menu, picture menu
- Drop-down list, menus/drop-down list
- Scrollable area, liste gerarchiche, list box
- Invisible button, highlight button, radio picture
- Termometri, ruler
- Tab control
- Splitter


Codice Codice Creare un Quick Report da programma
Ecco un esempio su come creare un quick report da programma:
1. viene creata un'area di appoggio offscreen (cioè non visibile all'utente)
2. viene impostata la tabella corrente
3. vengono inserite le colonne
4. si scelgono le colonne per l'ordinamento
5. si sceglie la destinazione, si esegue il Quick Report
6. si cancella l'area

Il codice stampa le colonne cognome e nome da una tabella Anagrafica e li ordina per cognome.

`Metodo: SempliceListaQR

C_LONGINT(mioQR)
mioQR:=QR New offscreen area ` 1
QR SET REPORT TABLE(mioQR;Table(->[Anagrafica])) ` 2
QR INSERT COLUMN(mioQR;1;->[Anagrafica]Cognome) `3, prima colonna
QR INSERT COLUMN(mioQR;2;->[Anagrafica]Nome) `3, seconda colonna
ARRAY REAL($colonne_ar;1) 4, preparo gli array
$colonne_ar{1}:=1 `4, ordina solo per la prima colonna
ARRAY REAL($ordinamento_ar;1)
$ordinamento_ar{1}:=-1 ` 4, -1 in ordine decrescente e 1 in ordine crescente.
QR SET SORTS(mioQR;$colonne_ar;$ordine_ar) ` 4
QR SET DESTINATION(mioQR;qr printer ) ` 5.
ALL RECORDS([Anagrafica]) ` 5.
QR RUN(mioQR) `5. questo esegue il report impostato!
QR DELETE OFFSCREEN AREA(mioQR) ` 6.
Web Web 4DScript e il limite dei 32K
Con il tag 4dscript puoi inserire in una pagina html il contenuto della variabile testo ritornata in $0.

Come fare se si vuole mandare più di 32K di testo?

Bisogna usareun 4DHTMLVAR dopo il 4DSCRIPT.

Per esempio:
<!--#4DSCRIPT/MioMetodoGetHTML/Mio_blob-->
<!--4DHTMLVAR Mio_blob-->
Nel metodo "MioMetodoGetHTML" Method, potresti ottenere il puntatore alla variabile blob passata in $1.
Se il puntatore è valido puoi riempire il Blob con fino a 2GB di dati (troppi per una pagina Html, cerca di metterne di meno :)
Successivamente i dati verranno mostrati dalla successiva 4DHTMLVAR.
Comandi Comandi Il comando BLOB size
Comando: BLOB size (blob) -> Longint

Categoria: BLOB

Versione: 6.0

Parametri: blob è una variabile o un campo di tipo BLOB

Risultato: Long Integer - Valore numerico indicante la dimensione del BLOB passato come parametro.

Descrizione: BLOB size ritorna la dimensione di un BLOB espressa in byte.

Esempio: il seguente frammento di codice aggiunge 100 byte al BLOB mioblob_bl

SET BLOB SIZE(BLOB size(mioblob_bl)+100)

Macro Macro Incollare testo commentato
La seguente macro permette di inserire il contenuto degli appunti come testo commentato in un metodo:



Macro_Paste_Comment



Ed ecco il metodo Macro_Paste_Comment

$text_t:=Get text from clipboard
If (Length($text_t)>0)
$text_t:="`"+Replace string($text_t;Char(13);Char(13)+"`")+Char(13)
TEXT TO BLOB($text_t;_blobReplace;Text without length )
_action:=2
End if

Info Info Register Client at Startup
L'opzione Register Client at Startup è impostata per default nella pagina Client/Server delle preferenze di 4d.
Ma se non si deve usare il comando EXECUTE ON CLIENT, è meglio togliere questa opzione che migliora le prestazioni del Client e riduce il numero di processi sul server, specialmente nel caso di molti utenti contemporanei.

Un altro suggerimento per migliorare le performance è di tenere nascosta la finestra dei processi del 4d Server che viene continuamente aggiornata con l'elenco dei sigoli processi dei singoli utenti, anche se in realtà nessuno la sta guardando.
Tecniche Tecniche Usare i numeri di tabella e campo
Quando si rende necessario passare una tabella o un campo come parametro si può utilizzare la forma

MioMetodo(3;5)

che indicherebbe la terza tabella e il quinto campo.

Il codice diventa però molto più leggibile (nonché trasportabile tranquillamente con l'Insider in altri progetti) se utilizziamo i comandi Table e Field che, quando ricevono come parametro un puntatore, ritornano i numeri di tabella e di campo rispettivamente. La forma da utilizzare diventa allora:

MioMetodo(Table(->[Tabella3]);Field(->[Tabella3]Campo5))

Codice Codice Individuare il tipo di formato della data
E' possibile capire il tipo di data utilizzato dal sistema su cui sta girando 4D usando questa unica riga di codice:

<>DateFormat:=Replace string(Replace string(Replace string(String(!10/20/99!;MM DD YYYY );"10";"M");"20";"D");"99";"Y")

Quando viene valutata su un computer che usa il sistema mese/giorno/anno, la variabile <>DateFormat conterrà il valore M/D/Y.
Eseguita su un computer che usi invece il sistema giorno/mese/anno, la variabile <>DateFormat conterrà il valore D/M/Y.
Si può usare un Case per gestire le possibilità.

Un sistema del genere è assai utili quando, ad esempio, risulta necessario inserire una costante di tipo data: possiamo determinare quindi se inserire 13/4/06 o 4/13/06.

Fonte: 4D Today

Plugin Plugin Stampa di un'area 4D Chart
Per stampare un'area di un grafico realizzato con 4D Chart si utilizza il comando CT PRINT, che corrisponde esattamente alla scelta della voce di menu "File" - "Print" della finestra/area Chart.

Il comando mostra di default la finestra di richiesta impostazioni per la stampa. Se non si desidera far comparire tale finestra, basta passare come terzo parametro (dopo l'id dell'area e un parametro che indica la posibilità di annullare la stampa) uno "0", che serve appunto a sopprimere la dialog di impostazione stampa.

Codice Codice Cancellazioni multiple nelle list box
Il concetto base è che l'oggetto ListBox corrisponde ad un array di valori booleani che diventano veri se l'utente seleziona una o più righe.

Uno dei vantaggi delle list box è quello di consentire cancellazioni multiple in base alla selezione. Ecco il semplice metodo che le realizza:

`lbarray è il nome della List Box
C_LONGINT($pos)

$pos:=0
Repeat
      $pos:=Find in array(lbarray;True;$pos)
           If ($pos>0)
           DELETE LISTBOX ROW(*;"lbarray";$pos)
           $pos:=0
           End if
Until ($pos<0)

Codice Codice Calcolo del checksum di un codice a barre EAN13 *
EAN13 è uno dei tipi di barcode più usati (il suo equivalente negli Stati Uniti è il codice UPC-A,che è un sottoinsieme di EAN13, ma dal 2005 i negozi americani devono accettare anche EAN13, eliminando di fatto la differenziazione).
Il tredicesimo carattere di un codice EAN13 è il codice di controllo del codice a barre, che viene calcolato utilizzando questo sistema:
- Si sommano le cifre del codice di posto pari
- Si aggiunge la somma delle cifre di posto dispari, ma moltiplicata per tre.
- Il checksum sarà il numero da aggiungere a questa somma per ottenere il primo multiplo di 10 immediatamente superiore.

Esprimiamo l'algoritmo di formato 4th Dimension:

$check:=0
For ($i;12;1;-1)
      If (($i/2)#($i\2))
           $check:=$check+(Num($String_to_encode[[$i]]))
      Else
           $check:=$check+(Num($String_to_encode[[$i]])*3)
      End if
End for

$check_S:=String($check)
$check:=10-Num(Substring($check_s;Length($check_s)))


Fonte: 4D Knowledgebase


2
Codice Codice Contare il numero di elementi di un file XML
Ecco un esempio di uso dei comandi Get First XML element e Get Next XML element per contare il numero di elementi di un file XML:

`Metodo ContaElementi
C_TEXT($1;$radice_t)
C_LONGINT($0;$2;$quanti_l)

$radice_t:=$1
$quanti_l:=$2+1

C_TEXT($subalterno_t)
$subalterno_t:=Get First XML element($radice_t)

While (OK=1)
    $quanti_l:=ContaElementi ($subalterno_t;$quanti_l) `chiamata ricorsiva
    $subalterno_t:=Get Next XML element($subalterno_t)
End while

$0:=$quanti_l

Comandi Comandi Il comando ARRAY TO SELECTION
Il comando ARRAY TO SELECTION, la cui sintassi è:

ARRAY TO SELECTION (array; field{; array2; field2; ...; arrayN; fieldN})

dove

array[n] sono gli array da copiare nella selezione;
filed[n] sono i campi che ricevono i dati (tutti appartenenti alla stessa tabella)

copia il contenuto di uno o più array in una selezione di record.
Il primo degli array passati determina il numero di record che verranno creati.
E' importantissimo ricordare che il comando SOVRASCRIVE LA SELEZIONE della tabella: se si vuol essere sicuri di non incappare in inconvenienti può essere utile eseguire un REDUCE SELECTION prima di ARRAY TO SELECTION.
Se invece si vuole intenzionalmente sovrascrivere la selezione, è bene controllare se qualche record è bloccato, guardando il contenuto del set del processo chiamato LockedSet, che contiene i record bloccati: i record bloccati non vengono sovrascritti da ARRAY TO SELECTION.
Il comando è ottimizzato per l'uso con 4D Server.

Codice Codice Il tasto Control su Mac e Win
I comandi Windows Ctrl down e Macintosh control down permettono di controllare se il tasto Control viene premuto sulle due piattaforme, ritornando True se il tasto è premuto. Il problema è che se in un software Mac si usa Windows Ctrl down, ritornerà True solo se è premuto il tasto Command, mentre Macintosh control down su Win non ha equivalenti e dunque ritorna sempre False. Per effettuare un controllo corretto si può allora utilizzare questo metodo:

C_LONGINT($platform)
C_BOOLEAN($0)
PLATFORM PROPERTIES($platform)
If ($platform=3)
    $0:=Windows Ctrl down
Else
    $0:=Macintosh control down
End if

Codice Codice Convertire un ottale in decimale
La conversione di un numero ottale in un decimale non è assai ricorrente, ma ecco comunque il metodo:

C_LONGINT($0;$decimal_l)
C_TEXT($1;$octal_t;$number_t;$octalStr_t)

$octalStr_t:=$1

$decimal_l:=0 `imposta un valore di ritorno

While (Length($octalStr_t)>0)
    $number_t:=Substring($octalStr_t;0;1)
    $octalStr_t:=Substring($octalStr_t;2)
    $decimal_l:=($decimal_l*8)+Num($number_t)
End while

$0:=$decimal_l

Info Info Risolvere i crash causati dal DEP di Windows Server
Se le applicazioni realizzate con 4D 2003.x appena lanciate si chiudono improvvisamente potrebbe essere colpa del DEP di Windows Server 2003 Service Pack 1.

La causa è il DEP (Data Execution Prevention) del Win 2003 SP1.
La soluzione:

1. Click su Start, click su Run, scrivere sysdm.cpl, e quindi click su OK.
2. Click sul tab Advanced, click su Performance, e quindi click su Settings.
3. In Performance Options, click sul tab Data Execution Prevention, e quindi click su Add.
4. Nella dialog box "Apri", scegliere il programma (4D Server).
5. Click su Open, click su Apply, e quindi click su OK. Quando viene richiesto di riavviare il computer, click su OK.

Tech note di MS agli indirizzi:
http://support.microsoft.com/default.aspx?scid=kb;en-us;875351
http://support.microsoft.com/kb/875351/it
http://support.microsoft.com/kb/899298/
Info Info Modifica della porta 4D WebServer
Per motivi di sicurezza si può rendere necessaria la modifica della porta utilizzata da 4D per i servizi Web, la 443. Per modificarla, nelle Preferenze di 4D 2004, andare in "Configuration" sotto "Web" e in "Web Server Publishing" è possibile modificare il numero di porta utilizzata.

L'elenco completo delle porte TCP utilizzate da Internet Command si trova qui.
Tecniche Tecniche Teoria della normalizzazione: dipendenze funzionali *
Cos’è una dipendenza funzionale? Analizziamo il seguente esempio:

[CAP]
CAP
Città
Provincia
Abbreviazione_nome_regione
Nome_Regione

Il CAP è un codice univoco di 5 caratteri. Cosa lo rende una chiave? Il fatto che esso determina tutti gli altri campi. Poiché per ogni CAP esistono una singola Città, una singola Provincia, ecc., questi campi sono in dipendenza funzionale rispetto al campo CAP.

Osserviamo poi gli ultimi due campi: l’Abbreviazione determina esattamente la Regione, cioè la regione è in dipendenza funzionale rispetto all’Abbreviazione, che ne fa dunque le veci di una chiave. Ma allora, se Abbreviazione_nome_regione è una chiave, questo campo deve appartenere ad un’altra tabella.

Ecco, la Terza Forma Normale (3NF) ci dice proprio che dobbiamo allora creare una tabella [Regioni] che avrà come campi Abbreviazione_nome_regione (chiave primaria) e Nome_Regione

Consulta da questo link l’indice delle faq sulla normalizzazione

2
Tecniche Tecniche 4d come WebService client
Per utilizzare 4d come client di un qualsiasi web service server, anche non un 4d ws, basta seguire questi passi:

1. cerca sul sito del web service server l'indicazione del corrispondente WSDL (Web Service Description Language); per i siti fatti con 4d è automaticamente http://dominio.it/4DWSDL.

2. in ambiente Design scegli il comando "Web Service Wizard" dal menu Tools

3. incolla l'url del WSDL del sito che interessa e clicca su Discover

4. scegli nell'elenco delle procedure quella che serve e fai clic su create

5. 4d genera il codice di consultazione del sito e lo mette in un metodo chiamato "proxy_nome" con l'indicazione dei parametri da passare e i dati ricevuti

La differenza (in parole molto povere) fra i server RPC e i server DOC è che i primi ritornano i parametri in modo semplice, mentre i secondi ritornano un documento XML da cui bisogna estrarre i dati che servono. La versione 2004 rispetto alla 2003 cerca di interpretare anche i server DOC e di estrarre i parametri: in entrambi i casi potrebbe sempre servire analizzare l'XML di risposta con i comandi forniti da 4D.

Riferimenti e altre info nella faq 4d come webservice server
Tecniche Tecniche 4d come WebService server *
Per realizzare un web service server con 4d basta:

1. attivare il web server (start web server da User o Publish Server at Startup nelle preferenze). Senza licenza web resta attivo per un'ora e poi bisogna chiudere e riaprire.

2. scegliere il Metodo che si vuole pubblicare e nelle proprietà attivare il flag "Allow Web Services Requests"

3. se si vogliono rendere i parametri in Input e in Output più facili da capire per l'utilizzatore usare il comando SOAP DECLARATION

4. l'utilizzatore usa il proprio client webservice (che sia 4d o un altro) interrogando il web service server attraverso il documento automaticamente generato all'url http://tuoserver/4DWSDL

Riferimenti da consultare:

4d e PHP
Client Soap su web
Un 4D web service server
Info Info Il file .4DA e i form modificabili dall'utente
Dalla versione 2004 è possibile rendere alcuni form modificabili in parte dall'utente finale.

Quando un form viene segnalato come "Editable by user", questo diventa bloccato nel Design environment. Ciò significa che lo sviluppatore deve esplicitamente "sbloccare" il form per poterlo utilizzare. Tale operazione rende però obsolete le modifiche effettuate dall'utente al form, che dovranno essere ricreate.

Le mofiche che l'utente apporta ad un form vengono memoorizzate in un file di tipo .4DA che si troverà allo stesso livello della struttura. Tale approccio permettere di rendere "non obsoleti" i possibile aggiornamenti alla struttura installati dal programmatore.

Codice Codice Ripristinare gli indici
Ecco un semplice metodo per ricostruire gli indici:

READ WRITE(*)
C_POINTER($table;$field)
C_LONGINT($i;$j;$type;$length)
C_BOOLEAN($indexed)

For ($i;1;Count tables)
    $table:=Table($i)
    For ($j;1;Count fields($table))
        $field:=Field($i;$j)
        GET FIELD PROPERTIES($field;$type;$length;$indexed)
        If ($indexed)
           SET INDEX($field->;False)
           SET INDEX($field->;True)
           FLUSH BUFFERS
        End if
    End for
End for

Alcune annotazioni:
- il metodo dovrebbe essere eseguito senza che altri utenti siano connessi e senza che se ne possano connettere prima del suo completamento;
- è possibile che la reindicizzazione necessiti di molto tempo;
- le subtable devono essere indicizzate a parte.

Se la reindicizzazione non andasse a buon fine, bisogna controllare quale campo comporta tale problema. Si potrebbe, ad esempio, prima della reindicizzazione di ogni campo, scrivere in un file su quali campi stiamo agendo, in modo da poter sapere quale dato ha portato al crash.

Tecniche Tecniche Tutte le faq sulla normalizzazione
Codice Codice Trovare un elemento in un documento XML usando XPath
4D 2003 introduce un set di comandi XML per scorrere la gerarchia "DOM" di un documento XML. Per trovare un elemento specifico occorre ciclare su tutti i nodi, e identificare i songoli elementi uno per uno.

In 4D 2004, il nuovo comando DOM Find XML element permette di trovare l'elemento usando la notazione XPath. Per esempio, poniamodi avere un documento XML con questa struttura:
<a>
  <b>
    <c>
      <d/>
      <e/>
    </c>
  </b>
</a>

Per trovare l'elemento "b" basterà scrivere:
$a:=DOM Parse XML source("mio.xml")
$b:=DOM Find XML element($a;"/a/b")

Allo stesso modo, per trovare l'elemento "e" a partire dall'elemento "a":
$e:=DOM Find XML element($a;"/a/b/c/e")

Oppure partendo dall'elemento "b":
$e:=DOM Find XML element($b;"/b/c/e")
Tecniche Tecniche Creare un'immagine lampeggiante *
Per creare un immagine lampeggiante in una maschera, segui questi passi::
1. Crea un nuova aimmagine nella Picture library.
2. Imposta l'immagine a 2 colonne e 1 riga
3. Incolla l'immagine nel secondo frame, lasciando il primo vuoto.
4. Trascina l'immagine così creata su un form per craere un picture button, chiamato ad esempio PulsanteImmagine.
5. Aggiungi il seguente codice nel metodo del form:
If (Form event=On Load)
  Set Timer(tickCount) `60esimi di secondo, da impostare secondo la velocità desiderata
End if
If (Form event=On Timer )
  If (PulsanteImmagine=0)
    PulsanteImmagine:=PulsanteImmagine +1
  Else
    PulsanteImmagine:= PulsanteImmagine -1
  End if
End if


1
Info Info Stampa di un subform con molti record
Ci sono tre opzioni "Print Frame" per stampare un subform, cioè una maschera a lista contenuta all'interno di un altra maschera (form), come ad esempio le righe all'interno della fattura.

L'opzione "Fixed (Truncation)" stampa il form una volta sola, e stampa il massimo numero di elementi correlati nella subform che rientrano nello spazio definito. Il resto degli elementi non viene stampato; questo serve ad avere un numero di pagine stampate fisso per ogni record, a prescindere dal numero dei record in relazione collegati.

L'opzione "Fixed (Multiple Records)" non modifica la dimensione del subforn in stampa, ma ristampa l'intera form tante volte quanto sia necessario a stampare tutti i record correlati mostrati nella subform.
Per esempio, se nella subform si possono mostrare 5 righe per volta e il record padre ha collegati 20 record, in stampa si avranno 4 pagine con 5 sotto-record differenti; questo permette di avere tutte le informazioni del record padre ristampate in ogni pagina.

L'opzione "Variable" ridimensiona il subforn in stama in modo tale da mostrare tutti i record correlati. Il form padre è stampato una volta solo, il subform viene esteso fino alla fine della pagina e può riprendere nelle pagine successive, finché necessario a stampare tutti i record. Questa opzione serve a stampare tutti i record quando non si ha bisogno di ripetere le stesse informazioni del record padre su ogni pagina,
Tecniche Tecniche Stampa dei campi negli header, break e footer
Quando si stampa utilizzando una maschera di un archivio, i record della tabella corrente vengono caricati dopo aver stampato l'area identificata dallo header (testata), dentro l'area detail (dettaglio), ripetutamente finché ci sono record in selezione. In fondo alla pagina viene ripetuto il footer (piede).

Stampando ad esempio delle fatture si potrebbe avere la seguente situazione: i campi del form della tabella corrente (ad esempio Modalità_pagamento o Totale) non vengono stampati nelle zone al di fuori del "Detail", ma i campi di altre tabelle (ad esempio i dati dell'archivio Clienti, che è in relazione) vengono visualizzati.

Così, risulta necessario visualizzare i dati relativi alla tabella corrente negli header, break e footer usando delle variabili. Gli assegnamenti possono essere effettuati, ad esempio, nel pulsante che genera la stampa.
Codice Codice Lanciare applicazioni senza mostrare le finestra
Nella 4D 2004, è stato introdotto il comando LAUNCH EXTERNAL PROCESS per eseguire applicazioni esterne in un processo separato e distinto da quello di 4d.

Su Windows, appare una finestra di terminale quando lanci un applicativo basato sulla console.
Nella 2004.1, il comando SET ENVIRONMENT VARIABLE è stato aggiornato per nascondere la finestra. Ecco come:

SET ENVIRONMENT VARIABLE("_4D_OPTION_HIDE_CONSOLE";"True")
LAUNCH EXTERNAL PROCESS("dir.exe";$input;$output)

Per rivedere la finestra basta lanciare di nuovo un LAUNCH EXTERNAL PROCESS senza il comando SET ENVIRONMENT VARIABLE o impostandolo a "False."
Codice Codice Chiedere la data all'utente
Solitamente l'utente che si vede davanti una finestra di Request che gli chieda l'inserimento di una data è costretto ad inserire 6,8,10 caratteri (con i separatori), con conseguente perdita di tempo, soprattutto se la data da inserire è (relativamente alla data attuale) sempre la stessa: se, ad esempio, l'utente inserisce solitamente la data di inizio del mese corrente o della settimana corrente, possiamo generare delle scorciatoie per avere un vantaggio dalla possibilità di inserire una stringa.
Ecco allora un metodo che gestisce una serie di scorciatoie da tastiera per le date. Le scorciatoie sono:

1) M : la prima data del mese
2) U : l'ultima data del mese
3) A : il primo giorno dell'anno
4) F : l'ultimo giorno dell'anno
5) L : il primo giorno della settimana
6) D : l'ultimo giorno della settimana
7) + : domani
8) - : ieri
9) O : oggi
10) Se l'utente inserisce un numero, viene ritornata la data con il mese e l'anno correnti
11) Se l'utente inserisce un numero, un separatore e un numero, viene ritornata la data con l'anno corrente
12) Altrimenti viene ritornato il risultato della funzione "Date" di 4th Dimension.

Ecco il metodo.


C_TEXT($1)
C_DATE($0)

$currentMonthString_S:=String(Month of(Current date(*)))
$currentYearString_S:=String(Year of(Current date(*)))
$firstDateOfThisMonth_D:=Date("01/"+$currentMonthString_S+"/"+$currentYearString_S)

Case of
    : ($1="O") ` oggi
        $0:=Current date(*)

    : ($1="A") ` primo giorno dell'anno
        $0:=Date("01/01/"+$currentYearString_S)

    : ($1="F") ` ultimo giorno dell'anno
        $0:=Date("31/12/"+$currentYearString_S)

    : ($1="M") ` primo giorno del mese
        $0:=$firstDateOfThisMonth_D

    : ($1="U") ` ultimo giorno del mese
        $0:=Add to date($firstDateOfThisMonth_D;0;1;-1)
           ` ho aggiunto un mese e tolto un giorno

    : ($1="L") ` primo giorno della settimena. Lunedì
        $data_D:=Current date(*)
        $miogiorno_L:=((Day number($data_D))-1)+(7*(Num(((Day number($data_D))=1)))
        $0:=Current date(*)-($miogiorno_L-1)
        `se si vuole Domenica usare direttamente
        `$0:=Current date(*)-(Day number(Current date(*))-1)

    : ($1="D") ` ultimo giorno della settimana. Domenica.
        $data_D:=Current date(*)
        $miogiorno_L:=((Day number($data_D))-1)+(7*(Num(((Day number($data_D))=1)))
        $0:=Current date(*)+(7-$miogiorno_L)
      ` se si vuole Sabato usare direttamente
      ` $0:=Current date(*)+(7-Day number(Current date(*)))

    : ($1="+") `domani
        $0:=Current date(*)+1
    : ($1="-") `ieri
        $0:=Current date(*)-1
    Else `nessuno dei casi precedenti
        $testDate_D:=Date($1)
        If ($testDate_D=!00/00/00!) `se non è una data completa la riempio io
           $1:=$1+"/"+$currentYearString_S
           $testDate_D:=Date($1)
           If ($testDate=!00/00/00!)
           $1:=$1+"/"+$currentMonthString_S+"/"+$currentYearString_S
           $testDate_D:=Date($1)
           End if ` ($testDate_D=!00/00/00!)
        End if ` ($testDate_D=!00/00/00!)
        $0:=$testDate_D
End case

Tecniche Tecniche Teoria della normalizzazione: la chiave primaria
Il concetto di “primary key” è fondamentale nella teoria dei database. Il concetto è semplice: ogni record deve avere qualcosa che lo identifichi univocamente.

La chiave primaria di una tabella serve inoltre come punto di partenza per la gestione delle relazioni con le altre tabelle: per esempio una fattura è un relazione con un unico e ben determinato cliente, come un impiegato è assegnato ad un unico e ben determinato ufficio.

Una chiave primaria deve avere un valore per ogni record.

La chiave primaria può essere formata da un campo o dalla combinazione di più campi. Questo dato dei database relazionali ha bisogno di una discussione a parte per quanto riguarda 4th Dimension: se molti sistemi di sviluppo per database usano esplicitamente il concetto di chiave primaria formata da più campi, ciò non avviene in 4D. In realtà è quasi banale bypassare questo limite aggiungendo alla tabella un campo che memorizzi la concatenazione dei campi che compongono la chiave primaria. Una chiave del genere può essere molto facilmente gestita utilizzando i trigger.

Una chiave primaria deve essere unica, obbligatoria e immodificabile: un classico errore è quello di attribuire il valore di chiave primaria a campi “volatili”. Ad esempio, per la tabella:

[Clienti]
Nome
Indirizzo

il campo Nome sembrerebbe un buon candidato come chiave primaria: sbagliato! Cosa succede se Nome cambia, ad esempio per correggere un semplice errore di inserimento? Dovremo cambiare tutti i record delle fatture di quel cliente.
Risulta molto più corretto usare un valore “esterno” ai dati reali, come Sequence number, o qualsiasi altro sistema che generi comunque dei valori da chiave primaria.

Per completezza sull’argomento diamo anche la definizione di superchiave, che non è altro che un insieme di campi per i quali la tabella non contiene due record con valori identici per tali campi (non è altro che il superinsieme di una chiave).
Inoltre in uno schema relazionale possono esserci più chiavi (ma solo una di esse verrà definita “principale”).

Consulta da questo link l’indice delle faq sulla normalizzazione

1
Comandi Comandi Come usare il Sequence Number
La funzione Sequence Number ritorna un numero progressivo automatico per ogni tabella ed è usato per creare il codice identificativo (o chiave primaria) del singolo record. Parte da 1 alla creazione del database ed è incrementata da 1 per ogni record salvato.

La funzione però aveva alcuni limiti per cui era sconsigliata: in pratica poiché non era possibile modificarne il valore corrente diventava difficile, ad esempio, spostare i dati da un database all'altro o ripartire da un certo numero.

Finalmente dalla 2004.1 è disponibile un modo accedere al valore usato dal Sequence Number:

1. Get Database Parameter ( Tabella ; Table Sequence Number ) -> Longint
Legge il valore corrente del prossimo numero che sarà assegnato dalla Sequence Number per la Tabella passata come primo parametro

2. SET DATABASE PARAMETER ( Tabella ; Table Sequence Number ; Longint )
Imposta il prossimo numero progressivo che sarà generato dalla Sequence Number pe la Tabella.


1
Plugin Plugin 2004.1: creare relazioni da linguaggio con AP Create relation
Il 4D Pack della 2004.1 permette di creare via linguaggio di programmazione le relazioni della struttura. La sintassi del comando AP Create relation è semplice:

AP Create relation (sourceTableNum; sourceFieldNum; destTableNum; destFieldNum)

prende cioè i numeri di tabella e di campo di partenza e di destinazione della relazione. Per le proprietà della relazione bisogna sempre agire dalla finestra delle proprietà della relazione.
Se l'operazione viene portata a termine con successo, la funzione ritorna 0, altrimenti un codice di errore.

Plugin Plugin Memorizzare le opzioni di stampa
Il comando AP Print settings to BLOB del 4D Pack 2004 permette di memorizzare in un BLOB passato come parametro le attuali impostazioni di stampa per 4th Dimension.
Il BLOB memorizza sia i parametri di layout (paper, orientation, scale) che gli altri parametri tipo (number of copies, paper source, ecc.)

Il BLOB non può essere modificato via linguaggio di programmazion, ma va richiamato usando il comando AP BLOB to print settings.

Il comando ritorna 1 se il BLOB è stato correttamente generato, altrimenti 0.

Codice Codice Identificare il tipo di un'immagine
Per conoscere il tipo di un'immagine in un documento o in un blob è possibile usare questo comando del plugin 4D Pack:

AP Get Picture type (Blob_immagine) -> longint

Il valore ritornato sarà:

-1 Sconosciuto
0 immagine 'PICT'
1 immagine 'PICT' compressa con QuickTime
2 immagine JPEG
3 immagine WMF
4 immagine EMF
5 immagine BMP
6 immagine GIF

Esempio:
DOCUMENT TO BLOB($nomeDocumento_t;$immagine_blb)
$tipo_l:=AP Get picture type($immagine_blb)
Tecniche Tecniche Eseguire una query fra due campi
Quando si vogliono confrontare due valori dei record di una tabella non è possibile usare la forma:

QUERY([Tabella];[Tabella]Campo1 < [Tabella]Campo2)

Questa forma non funziona. Deve utilizzarsi invece:

QUERY BY FORMULA([Tabella];[Tabella]Campo1 < [Tabella]Campo2)

Si deve in ogni caso tenere presente che tale operazione è più lenta, anche su campi indicizzati, poiché non si possono usare gli indici per eseguirla: 4D caricherà infatti ogni record per eseguire il confronto (valutare l'espressione passata come argomento) per poi aggiungerlo o meno alla selezione risultante.
Stile Stile Non mostrare valori quando un campo numerico vale zero
Quando si mostrano dei numeri in una maschera di output o in un report è possibile "nascondere" i campi che contengono semplicemente uno zero. Possiamo sfruttare infatti la possibilità di scrivere un formato del tipo "valore_positivo;valore_negativo;zero" per i numeri. Ad esempio, un formato possibile potrebbe essere:

"###.##0 ;###.##0-;"

I formati possono essere inseriti o direttamente nel campo o nel report, oppure creati e memorizzati nelle Database Properties.

Info Info Se andando in Design 4D va in crash
Capita (raramente, ma potrebbe capitare) che un metodo o una maschera (form) si danneggino e mandino in crash 4D quando vengono aperti. Ciò diventa realmente un problema quando, passando al Design, 4D cerca sempre di riaprire la finestra "danneggiata" perchè ricorda quale erano le finestre aperte dall'utente da una sessione all'altra.

Tenendo però premuto il tasto Alt (Option su Mac) quando si passa per la prima volta all'ambiente Design, 4D non aprirà nessuna delle finestre precedentemente aperte. Questo permette di selezionare poi l'oggetto nella finestra Explorer (avendo avuto cura di chiudere il pannello di Anteprima) e di eliminarlo.

Info Info Un AppleScript per lanciare 4D
Vediamo come si può lanciare un 4D Server usando un AppleScript. Supponiamo che:

- il disco di chiami "Titanium"
- la struttura si chiami "test"
- il data file is chiami "test.data"
- entrambi siano nella cartella "test"
- la cartella sia contenuta nella cartella "Documents" dell'utente "pierpaolo"

Laciare Script Editor e inserire questo codice, sostituendo al più i percorsi di 4D Server:

tell application "Finder"
    activate
    select file "Titanium:Users:pierpaolo:Documents:test:test"
    open selection using file "4D Server" of folder "4D Server" of folder "Applications" of startup disk
end tell

Salvare lo script e scegliere "Non mostrare all'avvio".
A questo punto aggiungere lo script agli elementi di avvio.

Web Web Tag HTML nidificati
Spesso può essere necessario nidificare i tag HTML. Ad esempio:

<input type ="text" name="varName" value = "<!--4DVAR [Table]Field-->">

è un esempio interessante poiché il tag nifificato è inserito all'interno delle virgolette. Poiché però le specifiche HTML non permettono di inserire tag all'interno di altri, per specificare se un check box è attivo NON si può scrivere:

<input type="checkbox" name="cbOne" value="Yes" <!--4DHTMLVAR isChecked-->>

dove la variabile isChecked ha il valore "checked".

Ovviamente, in casi come questo, si può usare un semplice stratagemma:

<!--4DIf (isChecked="checked")-->
<input type="checkbox" name="cbOne" value="Yes" checked>
<!--4DElse-->
<input type="checkbox" name="cbOne" value="Yes">
<!--4DENDIF-->

Codice Codice Aprire le finestre del Pannello di controllo con AP Sublaunch
Il comando AP Sublaunch del 4D Pack permette di eseguire una azione di sistema sotto Windows. Possiamo utilizzarlo quindi anche per aprire, ad esempio, finestre del pannello di controllo di Windows.

Per aprire il pannello di controllo:

$String_t:="rundll32 shell32.dll,Control_RunDLL"
$lErr:=AP Sublaunch ($String_t;0)

Per aprire una voce specifica del pannello di controllo, ad esempio il pannello di controllo dell'origine dei dati ODBC:

$String_t:="rundll32 shell32.dll,Control_RunDLL odbccp32.cpl"
$lErr:=AP Sublaunch ($String_t;0)


Alcune voci del pannello di controllo contengono più pagine, selezionabili attraverso un tab control. Possiamo anche da linguaggio specificare quale pagina aprire. Con l'esempio che segue viene aperta la pagina screen saver delle proprietà dello schermo:

$String_t:="rundll32 shell32.dll,Control_RunDLL desk.cpl,,1"
$lErr:=AP Sublaunch ($String_t;0)


Fonte: Jeffrey Kain, 4D Today

Tecniche Tecniche Mail avvelenata anti spam
Per chiunque abbia un sito, abbiamo già pubblicato una faq su come elaborare leggeremente l'indirizzo in modo tale che non sia facilmente recuperabile dai programmi che girano su inernet e recuperano le mail dal codice html.

C'è un'alternativa.. che è quella di includere nel tuo sito un indirizzo "avvelenato", cioè finto del tipo "billgates@miodominio.it" e lo affoghi nel codice html all'interno di un commento o in un font che lo rende non visibile all'utente normale.

Il programma che tira su gli indirizzi dai siti prende anche quello e poi tu filtri i messaggi che ti arrivano con la regola "blocca i messaggi in cui fra i destinatari c'è anche billgates@miodominio.it"
Info Info 4th Dimension e Mac OS 10.4 - Tiger *
La prima versione di 4D di comprovata compatibilità col nuovo sistema operativo di casa Apple, Tiger, sarà la 2004.2.

Per quel che riguarda le altre versioni al momento non si hanno notizie ulteriori.



1
Info Info Se i comandi da menu sembrano non funzionare...
Se i comandi da menu sembrano non funzionare, una delle cose che più probabilmente è successa è che abbiamo dimenticato di associare la menu bar ad una maschera (o form).

Supponiamo infatti che la nostra applicazione parta usando il menu bar 1.

Quando apriamo il processo relativo ad una certa tabella usiamo il comando MENU BAR(2) per dire al processo di usare la menu bar numero 2, che viene correttamente visualizzata ma, se usata, non produce alcun risultato.

Ciò che manca è l'associazione della menu bar al form: andare sulle proprietà del form e alla voce Menu bar scegliere la menu bar #2

Da notare che nelle ultime versioni è possibile dare un nome alle Menu Bar: usando il nome ne guadagna molto la comprensibilità del codice.
Codice Codice Creare un nuovo metodo da programma con 4D Pack
Nel 4D Pack della 2004.1 c'è un nuovo comando AP Create Method che permette di creare un nuovo metodo in un database interpretato.

La sintassi è la seguente:
codice_errore:=AP Create Method(NomeMetodo;arrayAttributi;blobCodice;nomeCartella)

Ecco un esempio:
C_LONGINT($err_l)
C_TEXT($nomeMetodo_t;$nomeCartella_t)
ARRAY LONGINT($attributi_al;4)
C_BLOB($codice_blb)
$nomeMetodo_t:="Mio Nuovo Metodo"
TEXT TO BLOB("ALERT(\"Ciao\")";$codice_blb;Text without length;*)
$attributi_al{1}:=1 ` è usabile come 4D action?
$attributi_al{2}:=1 ` è invisible?
$attributi_al{3}:=1 ` è usabile da soap?
$attributi_al{4}:=1 ` è pubblicato nel wsdl?
$nomeCartella_t:="Default Folder"
$err_l:=AP Create method($nomeMetodo_t;$attributi_al;$codice_blb;$nomeCartella_t)

err_l contiene 0 se è andato bene e -1 se c'è stato un errore. Da ricordarsi che funziona solo su database interpretati, non compilati.
Info Info Impostare in automatico il formato di stampa
Una delle informazioni che un form è in grado di memorizzare in fase di design è il formato di stampa in cui si desidera stampare, dove per formato di stampa non dobbiamo intendere semplicemente l'orientazione del foglio, orizzontale o verticale, ma anche la vera e propria dimensione del foglio, il che permette di utilizzare facilmente anche formati di carta non standard.

Novizi Novizi Puntatori
I puntatori sono uno strumento di programmazione avanzata, che principalmente permettono di scrivere del codice "generalizzato".

Tecnicamente un puntatore contiene un riferimento ad una tabella, campo, variabile di qualsiasi tipo compresi gli array o vettori. Ecco un esempio di puntatore ad una variabile:

C_TEXT(MiaVariabile_t)
C_POINTER(punta_ptr)
MiaVariabile_t:="Umberto Migliore"
punta_ptr:=->MiaVariabile_t

a questo punto:
ALERT(punta_ptr->) è come scrivere ALERT(MiaVariabile_t)

Ad esempio, se scrivo una procedura generica per la gestione dei pulsanti di una maschera posso usare una variabile di tipo puntatore invece di indicare la variabile stessa.

Se ad esempio scrivo un pezzo di codice come il seguente

If (Condizione) ` se la condizione è Vera…
  ENABLE BUTTON (MioPulsante) ` abilita questo pulsante
Else ` altrimenti…
  DISABLE BUTTON (MioPulsante) ` disabilitalo
End if

dovrò ripeterlo se ho molti pulsanti nel form.
Invece è possibile scrivere un metodo generico "ImpostaPulsante" così:

  ` metodo IMPOSTA PULSANTE
  ` IMPOSTA PULSANTE ( Pointer ; Boolean )
  ` IMPOSTA PULSANTE ( ->MioPulsante ; Abilita )
  `
  ` $1 – Puntatore al pulsante
  ` $2 – Booleano. Se TRUE, abilita il pulsante. Se FALSE, lo disabilita

If ($2)
  ENABLE BUTTON($1->)
Else
  DISABLE BUTTON($1->)
End if
Comandi Comandi Impostare il timeout di una chiamata SOAP
Nelle chiamate SOAP con Call Web Service il timeout di connessione è di default 10 secondi: se il server non risponde in questo tempo, il client chiude la connessione.

Per impostare un timeout differente per la prossima chiamata è possibile usare il seguente comando (disponibile dalla versione 2004):

SET WEB SERVICE OPTION(Web Service HTTP Timeout;30)

Se non si riesce ad utilizzare questa opzione, magari perchè la connessione cade lo stesso malgrado un timeout più lungo, allora si può provare ad ottenere il risultato
della propria chiamata in differita.

Cioè, ipotizzo una comunicazione del genere:
          
A) il client : invia il dato
B) il server : riceve e risponde subito un codice "sto elaborando"
C) il client : ogni tempo X riprova a chiedere lumi al server
D) il server : quando ha finito il codice è "ok" oppure "err, lista errori"

Se il client (e l'utente) deve rimanere bloccato in attesa, il tempo X potrebbe essere molto breve, anche di 5 secondi.
Codice Codice Uso dei semafori
Quando si vuole essere certi che una certa operazione sia eseguita senza contemporaneità è possibile usare la funzione Semaphore.

Questa funziona permette di testare se un semaforo esiste già (ritorna TRUE) oppure lo crea (e ritorna FALSE). Eseguite le sue operazioni lo si cancella con Clear Semaphore.

Ecco un esempio di codice per usarlo:

If (Semaphore("ModificaDelListinoPrezzi")) ` Provo a creare il semaforo
  ALERT("Qualcun altro sta già modificando i prezzi. Riprova più tardi.")
Else
  ` Metti qui il codice che devi eseguire
  CLEAR SEMAPHORE("ModificaDelListinoPrezzi")) ` Non mi serve più
End if

Oppure per una modifica che coinvolge operazioni di breve durata, faccio aspettare la mia richiesta finché non si libera il posto:

C_Boolean($Fatto_b)
$Fatto_b:=False
While ($Not($Fatto_b))
  If (Semaphore("RegistroNumFattura"))
    IDLE
  Else
    ' Metti qui il codice che devi eseguire
    CLEAR SEMAPHORE("RegistroNumFattura")
    $Fatto_b:=True
  End if
End while

Info Info Problemi conosciuti fra 4D e le stampanti HP con driver PCL
Esiste un problema di compatibilità fra alcuni driver PCL per stampanti HP sotto Windows e 4th Dimension. In questi casi, la cosa migliore è utilizzare un driver di tipo PostScript. Se la stampante non supportasse questo linguaggio, è consigliabile usare un driver Microsoft per stampanti HP invece del driver distribuito insieme alla stampante.
Codice Codice Comunicazione via porta seriale
Le comunicazioni attraverso le porte seriali tipo RS 232, RS 422 o RS 485 sia in trasmissione che in ricezione possono essere gestite dallo stesso metodo. Eccone un esempio.
Utilizzaremo le seguenti variabili:

<>Port è la porta da usare per la trasmissione/ricezione.

<>Param sono le impostazioni della porta seriale (velocità, data bit, etc.).

<>In_Out: controlla l'esecuzione del While.

<>vTransmit è la stringa da trasmettere.

<>tPages è un array testo per gestire ricezioni maggiori di 30.000 caratteri.

Ecco il metodo:

C_TEXT($vRecept)
C_TEXT($ReadMem)

ARRAY TEXT(<>tPages;1)
<>PageNumber:=1

SET CHANNEL(<>Port;<>Param)
SET TIMEOUT(<>Time_Out)
<>In_Out:=True

While (<>In_Out)
      RECEIVE BUFFER($vRecept)

      Case of
           : ($vRecept#"")  ` dati ricevuti
           $ReadMem:=$ReadMem+$vRecept
           <>ReadVar:=<>ReadVar+$vRecept
           <>LongVarRead:=Length(<>ReadVar)

           If (Length($ReadMem)>30000)
           <>tPages{<>PageNumber}:=$ReadMem
           $ReadMem:=""
           <>ReadVar:=""
           <>PageNumber:=<>PageNumber+1
           CALL PROCESS(1)
           INSERT ELEMENT(<>tPages;Size of array(<>tPages)+1)
           End if

           CALL PROCESS(-1)

           : (<>vTransmit#"")  ` trasmissione dati
           BEEP
           SEND PACKET(<>vTransmit+Char(13))
           <>vTransmit:=""
           CALL PROCESS(-1)

      End case

      DELAY PROCESS(Current process;30)
End while

SET CHANNEL(11)


In caso di alti flussi di dati potrebbe essere necessario ridurre il tempo di pausa inserito nella riga:

DELAY PROCESS(Current process;30)

Codice Codice Anticipare l'inserimento di una parola
Ecco un metodo semplice per anticipare cosa sta scrivendo l'utente in un campo testo, ipotizzando che il contenuto possa già trovarsi in una tabella a disposizione: ad esempio, per fare un filtro su una sottolista o per suggerire un termine già inserito o un termine fra una lista definita.

Dato un oggetto parola_t, bisogna attivare l'evento "on after keystroke": da considerare che questo è chiamato prima di uscire dal campo, quando la variabile parola_t non contiene niente; per questo prendiamo il valore con la funzione "get edited text"

If (Form event=On After Keystroke)
  caratteri_t:=Get edited text
  If (caratteri_t#"")
    QUERY([Tabella];[Tabella]ParolaChiave = caratteri_t +"@")
    SELECTION TO ARRAY([Tabella]ParolaChiave; listaParole_at)
    UNLOAD RECORD([Tabella])
  Else
    REDUCE SELECTION([Tabella];0)
    ARRAY STRING(50;listaParole_at;0)
  End if
End if
Novita' Novita' Ottenere l'elenco degli oggetti di un form
Il nuovo comando della versione 2004 GET FORM OBJECTS permette di popolare tre array (passati come parametro) che elencano gli oggetti presenti in un certo form. Ciò risulta molto utile se l'aspetto del form deve essere gestito in modo dinamico. La sintassi è del tipo:

GET FORM OBJECTS($arrOggetti_at;$arrVariabili_aptr;$arrPagine_ai)

dove i tre array contengono i nomi degli oggetti, i relativi puntatori ad essi e il numero di pagina in cui si trovano. Una seconda sintassi prevede l'aggiunta del parametro "*" che forza la restituzione degli oggetti relativi alla sola pagina corrente del form.
Info Info Cambiare il nome di un metodo
Qualsiasi sia la ragione, quando si decide di cambiare il nome di un metodo in una struttura, bisogna assicurarsi di averlo fatto dovunque.

Il sistema principale per trovare tutte le occorrenze in cui è usato un metodo è usare il comando "Find in Database..."; comunque non è del tutto completo.

Ecco un elenco di altri posti dove guardare:
- nella finestra dove si impostano i Menu
- nella finestra delle password di 4D (se ci sono metodi eseguiti all'avvio dell'utente)
- nelle Etichette (Label Editor), dove è possibile indicare un metodo da eseguire quano si stampa un'etichetta
- nelle Ricerche (Query By Formula), dove puoi inserire il metodo come una stringa ed eseguirlo durante la ricerca
- nei Rapporti (Report), dove è possibile inserire un metodo come formula di una colonna
Plugin Plugin Importando da Excel via ODBC perdo la prima riga
Usando ODBC, Microsoft Excel ritiene che la prima riga del foglio di calcolo contenga i nomi dei campi: quindi, per questo motivo, l'importazione dei dati in 4D effettuata via ODBC non "prende" il primo record. E' importante notare come questo comportamento non sia un errore, ma rientri in realtà nello standard ODBC, per il quale è necessario avere un nome per ogni colonna importata: ciò per evitare qualsiasi problema.

Come soluzione si può pensare di inserire nel foglio di calcolo, prima dell'importazione, una riga di celle vuote: tale riga verrà considerata da ODBC come la riga contenente i nomi delle colonne.
La riga di celle vuote può essere inserita a mano oppure via linguaggio di programmazione usando il plug-in di 4th Dimension DDE Tools.
Codice Codice Importare/esportare campi con immagini
Una soluzione al problema è quella di utilizzare i comandi IMPORT DATA e EXPORT DATA. Prima però è necessario creare un progetto di import/export in questo modo:

- aprire l'editor di import/export;
- scegliere i campi;
- scegliere se esportare tutto o la selezione (meglio la selezione, non si sa mai);
- nella sezione "Format" scegliere 4th Dimension;
- salvare il progetto cliccando sul pulsante di salvataggio delle impostazioni ("Save settings") nella stessa cartella della struttura (nel nostro esempio supporremo di aver salvato il progetto col nome "IEProject");
- premere "Cancel" per uscire.

A questo punto, i due metodi che seguono permettono importazione ed esportazione dei dati:

`Metodo: Import
C_BLOB($bProject)
DOCUMENT TO BLOB("IEProject";$bProject) `Carica l'Import/Export Setting
IMPORT DATA("FileName";$bProject)

`Metodo: Export
C_BLOB($bProject)
DOCUMENT TO BLOB("IEProject";$bProject) `Sempre Carica
EXPORT DATA("FileName";$bProject)
Novizi Novizi Se un'immagine non viene visualizzata
Quando un'immagine non viene visualizzata, molto probabilmente questo significa che il tipo di pict non è supportato dalla macchina, o meglio dalla sua configurazione software: in genere basta installare Quicktime (che è gratuito) o anche solo aggiornarlo all'ultima versione.

Per conoscere i tipi di file immagine supportati si usa il comando:

PICTURE TYPE LIST (formatArray{; nameArray})

che riempie gli array passati come parametro con i codici di import/export di QuickTime che la macchina supporta.
Diventa facile così controllare se il tipo della pict in questione è supportato.
Comandi Comandi SET TABLE TITLES e SET FIELD TITLES *
Il comando SET TABLE TITLES permette di nascondere, riordinare o rinominare le tabelle presenti nel database quando questo elenco appare nelle dialog di 4th Dimension, come ad esempio il Query Editor o Quick Report.

La sintassi è:

SET TABLE TITLES (tableTitles; tableNumbers)

dove tableTitles è l'array contenente i nuovi nomi e tableNumbers le attuali posizioni nella struttura (table number).

Gli array devono essere sincronizzati. Per non far comparire una tabella basta non includerne titolo e numero negli array.

Quindi, ad esempio supponiamo di avere tre tabelle A, B e C, create in questo ordine, e di volerle far apparire come X, Y e Z. Inoltre non vogliamo che la tabella B sia visibile. Infine, vogliamo far apparire le tabelle nell'ordine Z e X. Tutto ciò si fa semplicemente inserendo Z e X nell'array dei nomi e 3 e 1 nell'array delle posizioni.

SET TABLE TITLES non modifica la struttura, ma è valido per le semplici sessioni: quindi ad esempio, più 4D Client possono vedere lo stesso database simultaneamente in modo differente.

SET TABLE TITLES non ha il potere di modificare l'attributo Invisible attribuito ad una tabella nella struttura: così, una tabella invisibile continuerà ad esserlo, anche se inserita negli array passsati al comando.

Il comando SET FIELD TITLES è molto simile, semplicemente applica le stesse considerazioni di SET TABLE TITLES ai campi di una tabella.


2
Tecniche Tecniche Uso di Day number col calendario europeo
La funzione Day number segue la convenzione americana relativa alla numerazione dei giorni: 1 è domenica e 7 è sabato.

Per convertire tale numero in formato europeo (dove il giorno numero 1 è lunedì e il 7 è la domenica) basta fare:
$miogiorno_l:=((Day number($data_d))-1)+(7*(Num(((Day number($data_d))=1)))
Codice Codice Ripulire i dati dopo una importazione DBF
Se capita di dover sviluppare un programma 4D che abbia come dati quelli presenti in archivi DBF, quando viene eseguita l'importazione degli archivi DBF i dati di tipo stringa contengono degli spazi finali inutili. Una tecnica per risolvere il problema è eseguire il presente codice di correzione:

For ($tableNum_l;11;20) `se, ad esempio, le prime 10 tabelle non sono importate
      For ($campoNum_l;1;Count fields($tableNum_l))
           GET FIELD PROPERTIES($tableNum_l;$campoNum_l;$tipoCampo_l)
           If ($tipoCampo_l =0)
           $table_ptr:=Table($tableNum_l)
           ALL RECORDS($table_ptr->)
           $campo_ptr:=Field($tableNum_l;$campoNum_l)
           APPLY TO SELECTION($table_ptr->;$campo_ptr->:=EliminaSpazi ($campo_ptr->))
           End if
      End for
End for

Il metodo EliminaSpazi si trova in questa faq.
Codice Codice Estrarre i nomi e i valori da un url
Poniamo di avere una stringa nella forma "http://www.sviluppo4d.it?arg=1&arg2=2&arg3=3". Questa stringa è facilmente spezzata in due array di nomi e valori corrispondenti con il comando GET WEB FORM VARIABLES.

Ma in alcuni casi, per risparmiare spazio nella stringa (gli URL sono limitati a 255 caratteri) la stringa potrebbe essere del tipo "http://www.sviluppo4d.it?parametro1=500&miocomando", cioè potrebbe mancare il valore con il suo "=".

In questo caso si può usare questo metodo, che accetta come parametri la stringa e il puntatore dei due array, dei nomi e dei valori.

`Metodo: EstraiNomiEValori

C_TEXT($1;$testoParametri_t;$stringa_t)
C_POINTER($2;$Nomi_ptr)
C_POINTER($3;$Valori_ptr)
C_TEXT($sinistra_t;$destra_t)

$testoParametri_t:=$1
$Nomi_ptr:=$2
$Valori_ptr:=$3

    ` se troviamo un'altra & nella stringa la gestiamo richiamndo lo stesso metodo
If (Position("&";$testoParametri_t)#0)
    $stringa_t:=Substring($testoParametri_t;0;Position("&";$testoParametri_t)-1)
    $testoParametri_t:=Substring($testoParametri_t;Position("&";$testoParametri_t)+1)
    EstraiNomiEValori($testoParametri_t;$Nomi_ptr;$Valori_ptr)
Else
    $stringa_t:=$testoParametri_t
End if

    ` Qui inizia il lavoro vero e proprio
If (Length($stringa_t)>0)
    ` controlla se si tratta di un assegnamento normale o solo di un nome
    If (Position("=";$stringa_t)#0)
        $sinistra_t:=Substring($stringa_t;0;Position("=";$stringa_t)-1)   ` parte sinistra
        $destra_t:=Substring($stringa_t;Position("=";$stringa_t)+1)   ` parte destra
    Else
        $sinistra_t:=$stringa_t   ` solo la parte sinistra, senza la parte destra
    End if

    INSERT ELEMENT($Nomi_ptr->;1)
    INSERT ELEMENT($Valori_ptr->;1)

    $Nomi_ptr->{1}:=$sinistra_t
    $Valori_ptr->{1}:=$destra_t
End if
Codice Codice Spostamento di una sottolista gerarchica
Ecco una metodo semplice per gestire lo spostamento di una sottolista all'interno di una lista Gerarchica. L'oggetto che contiene la lista gerarchica avrà questo codice:

If (Form event=On Drop )
        SpostaSottolista(Self->)
        REDRAW LIST(Self->)
End if

Ed ecco il codice del metodo SpostaSottolista:

C_LONGINT($1;$Lista_l)
C_LONGINT($oggettoOrig_l;$elementoOrig_l;$processoOrig_l)
C_LONGINT($riferimentoOrig_l;$riferimentoDest_l;$sottolista_l)
C_TEXT($testoOrig_t;$testoDest_t)
C_BOOLEAN($aperta)
$Lista_l:=$1

  `prendo i dati della posizione di partenza e di quella di arrivo
DRAG AND DROP PROPERTIES($oggettoOrig_l;$elementoOrig_l;$processoOrig_l)
GET LIST ITEM($Lista_l;$elementoOrig_l;$riferimentoOrig_l;$testoOrig_t;$sottolista_l;$aperta_b)
GET LIST ITEM($Lista_l;Drop position;$riferimentoDest_l;$testoDest_t)

  `cancello la partenza e inserisco i dati nella posizione di arrivo
DELETE LIST ITEM($Lista_l;$riferimentoOrig_l)
INSERT LIST ITEM($Lista_l;$riferimentoDest_l;$testoOrig_t;$riferimentoOrig_l;$sottolista_l;$aperta_b)
Plugin Plugin Ottenere l'ora di modifica dei file su server FTP
Finalmente dalla versione 2003.6 e dalla versione 2004.1 sono stati modificati due comandi facenti parte del pacchetto Internet commands, e precisamente il comando 'Ftp_GetDirList' e anche il comando 'Ftp_GetFileInfo'.

La modifica che è stata apportata riguarda la possibilità di avere nei vari array di ritorno del comando FTP_GetDirList (ftp_ID; directory; names; sizes; kinds; modDates; modTimes): Integer il nuovo array 'modTimes': è un vettore di interi lunghi che ritornano il valore dell'ora di modifica dei file espresso in secondi.
Quindi per esempio, se l'ora di modifica è 10:08 (hh:mm) nel vettore moTimes avremo 3600 x (10) + 60 x (8) = 36.480).

Stesso concetto per il comando FTP_GetFileInfo (ftp_ID; hostPath; size; modDate; modTime): Integer che compila il nuovo parametro modTime con l'ora di modifica del singolo files, di tipo time (hh:mm).

Codice Codice Lista dei processi attivi
La funzione Count tasks ritorna il numero totale dei processi che sono stati aperti in un'applicazione 4D, inclusi i processi ormai chiusi. Ecco una semplice procedura che riempie due array con i nomi e i corrispondenti numeri dei processi attivi: la regola che usa è che lo stato del processo è 0 se in esecuzione, positivo se in pausa o in qualche altro stato di attesa, negativo se non esiste o se ha finito.

C_LONGINT($quantiProcessi_l;$quantiAttivi_l;$processo_l;$stato_l;$tempo_l)
$quantiProcessi_l:=Count tasks

ARRAY STRING(31;procNomi_at;$quantiProcessi_l)
ARRAY INTEGER(procNum_al;$quantiProcessi_l)
$quantiAttivi_l:=0
For ($processo_l;1;$quantiProcessi_l)
    If (Process state($processo_l)>=Executing )
        $quantiAttivi_l:=$quantiAttivi_l +1
        PROCESS PROPERTIES($processo_l;$procNomi_at{$quantiAttivi_l};$stato_l;$tempo_l)
        $procNum_al{$quantiAttivi_l}:=$processo_l
    End if
End for
ARRAY STRING(31;$procNomi_at;$quantiAttivi_l)
ARRAY INTEGER($procNum_al;$quantiAttivi_l)
Codice Codice Trasformare un blob in un array di testo
  `Nome Methodo: BLOB_to_Text_Array
    `Descrizione: Riempe un array di testo con i dati contenuti in un BLOB
    `Parametro: $0 ritorna il numero di elementi creati nell'array
    `Parametro: $1 è un puntatore ad una variabile di tipo BLOB
    `Parametro: $2 è un puntatore ad una variabile di tipo Text Array (da riempire)

C_POINTER($1;$2)
C_LONGINT($dimBlob_l;$dimRiga_l;$dove_l;$0)

$dimRiga_l:=20000  ` dimensione di ogni blocco di testo (massimo è 32000)
$dove_l:=0
$dimBlob_l:=BLOB size($1->)

Repeat
  APPEND TO ARRAY($2->;BLOB to text($1->;Text without length ;$dove_l;$dimRiga_l))
Until ($dimBlob_l<=$dove_l)

$0:=Size of array($2->)  ` ritorna il numero di elementi aggiunti
Info Info Dove mettere i certificati per SSL
Se si usa SSL con 4th Dimension o 4D Server come Web Server, i file key.pem e cert.pem dovranno essere posizionati nella cartella della struttura, allo stesso livello di questa.

Se si usa 4D Client come Web Server, invece, i file devono essere posizionati nella cartella di 4D Client su Win, all'interno del package 4D Client su Mac.
Codice Codice Velocizzare le liste di output
4D fa passare nelle liste di output solo i campi visibili, a meno che non ci sia uno script che richieda un campo che nella prima riga non c’era: a quel punto 4D carica tutti i campi del record. Questo può essere un problema se la tabella ha molti campi o se contiene un campo con un'immagine o un blob.

Soluzione: basta caricare in ogni riga, al display detail, tutti i campi che servono anche quando non li si usa. In questo modo la lista è nettamente più efficiente.

Ad esempio, se nella lista c'è un codice così:

:(form event=on display detail)
  vTaglia:=[Animale]Altezza
  If ([Animale]Razza=”Pachiderma”)
    vTaglia:=[Animale]Peso
  End if

Basta scriverlo in questo modo

:(form event=on display detail)
  vTaglia:=[Animale]Altezza
  $campoPeso:=[Animale]Peso
  If ([Animale]Razza=”Pachiderma”)
    vTaglia:=$campoPeso
  End if

In questo modo 4d può già dalla prima riga mettere in conto quali sono i soli campi necessari a mostrare la lista.
Novita' Novita' Il comando DISPLAY NOTIFICATION
Il comando (della 2004) DISPLAY NOTIFICATION permette, sotto Windows, di visualizzare un messaggio nella barra delle applicazioni, per intenderci accanto all'orologio!
Ecco una immagine esplicativa proveniente dal sito 4D:



La sintassi è:

DISPLAY NOTIFICATION (title; text{; duration})

dove title è il titolo della notifica (la parte in grassetto), text il testo del messaggio e, opzionalmente, duration è la durata del messaggio.
Stile Stile I nomi dei semafori
Il nome di un semaforo è una semplice stringa: per evitare di creare semafori non voluti oppure di non cancellare semafori creati è buona prassi attribuire il nome del semaforo ad una variabile e poi utilizzare quest'ultima nei comandi successivi. Ad esempio:

$SemaphoreName:="IlSemaforo"
If (Not(Semaphore($SemaphoreName))

    ` quel che vuoi

    CLEAR SEMAPHORE($SemaphoreName)
End if

Codice Codice Modificare l'input da tastiera
Supponiamo di avere il seguente problema: voler utilizzare il tasto "Canc" del tastierino numerico come "." e non come "," per ottenere un input da tastiera del tipo "ABC.123.456" invece di "ABC,123,456".
La soluzione è utilizzare i comandi che permettono di intercettare l'inserimento da tastiera da parte dell'utente:

- il comando Keystroke restituisce in una stringa il carattere inserito dall'utente;
- il comando FILTER KEYSTROKE prende come parametro una stringa e sostituisce il testo inserito dall'utente col primo carattere della stringa passata come parametro;
- il form event On Before Keystroke che si attiva quando l'utente "sta per digitare" qualcosa sulla tastiera.

Mettendo insieme tutte queste informazioni il codice diventa:

Case of
      :(Form event=On Load )
           myObject_s:=""
      :(Form event=On Before Keystroke)
           If (Position(Keystroke;",")>0)
           FILTER KEYSTROKE(".")
           End if
End case


Per altre possibilità per intercettare l'input dell'utente è possibile vedere anche questa faq
Plugin Plugin Eseguire funzioni Oracle usando comandi ODBC
Supponiamo di voler eseguire la funzione CIOCCOLATA presente in un database Oracle e di voler ricevere il valore ottenuto in una varaibile 4D. Per fare questo bastano i comandi ODBC, da usare come come segue:

$sqlstmt:="SELECT CIOCCOLATA FROM DUAL"
ODBC LOGIN("data";"nome";"pass")
ODBC EXECUTE($sqlstmt;vlresult)
ODBC LOAD RECORD `il risultato viene caricato in vlresult
ODBC LOGOUT

Se la funzione avesse bisogno di un parametro, potremmo anche passarlo in maniera dinamica:

vlinput:=5
$sqlstmt:="SELECT CIOCCOLATA(:vlinput) FROM DUAL"
ODBC LOGIN("data";"nome";"pass")
ODBC SET PARAMETER(vlinput;ODBC Param In )
ODBC EXECUTE($sqlstmt;vlresult)
ODBC LOAD RECORD
ODBC LOGOUT
Tecniche Tecniche Ricreare gli indici
Una tech tip della Knowledgebase di 4D consiglia questo metodo per effettuare una reindicizzazione completa ed accurata dei dati. Ovviamente non è un'operazione da fare tutti i giorni, ma potrebbe servire in emergenza quando ad esempio si aggiorna un database da una versione molto datata.

Il procedimento suggerito è il seguente:

- duplicare la struttura
- aprire questa compia utilizzando una nuova base dati
- eliminare manualmente tutte le relazioni
- eliminare manualmente gli indici dai campi indicizzati
- chiudere questa struttura aprirla nuovamente, stavolta però sui dati originali
- chiudere nuovamente tutto e riaprire la struttura originale sui dati originali: gli indici verranno così completamente ricostruiti.

Info Info Una nuova immagine per lo splash screen
Con 4D 2004 è cambiato anche il modo di di associare l'immagine di partenza alla nostra struttura. Non è più necessario incollare l'immagine nell'anteprima, ma basta scegliere Design -> Toolbox -> Menus e nell'angolo in basso a destra cliccare per ottenere uno splash screen che chiede se vogliamo effettuare l'"Incolla" della clipboard:

screen shot


Codice Codice Chiudere il 4D server interagendo con le dialog
Nota UM: Dalla 6.8 è disponibile il comando Quit che rende obosleta questa Faq, ma il metodo utilizzato (Post Key) è interessante e utilizzabile in qualche altro caso.

Ecco un metodo che permette di eseguire l'uscita da un 4D Server direttamente da 4D Client, senza dover quindi accere alla macchina dove il Server è installato: l'unico parametro utilizzato è il tempo di attesa che si vuole date al Server prima di chiudersi.

C_INTEGER($1)
    `
C_STRING(11;$attesa_stringa) ` tempo di attesa come stringa
C_INTEGER($I_Attesa;$I_ProcessNum;$i)
C_BOOLEAN($B_WaitingUserDisconn) ` True = wait attende l’uscita
    `
    `
Case of
      ` su 4D Client, lanciamo lo stesso metodo, ma come Stored procedure
  : (Application type=4D Client )
  $I_Attesa:=-2 ` -2 means "Default 4D Server's value" (see below)
  If (Count parameters#0)
    $I_Attesa:=$1
  End if
  $I_ProcessNum:=Execute on server(Current method name;32*1024;Current method name;$I_Attesa)
      `
      ` su Server generiamo un Command o Ctrl - Q
  : (Application type=4D Server )
  $B_WaitingUserDisconn:=False
  $attesa_stringa:=""
  Case of
    : (Count parameters=0) ` possibile se chiamato altrove
    : ($1<0) ` se negativo usa l’opzione di default
    $B_WaitingUserDisconn:=($1=-1) ` cioè attendo l’uscita
  Else
    $attesa_stringa:=String($1) ` per fare in modo che la cifra venga scritta con POST KEY
  End case
      `
      ` Mando l’evento al processo numero 1.
  POST KEY(Ascii("q");Command key mask ;1)
      ` attendo che compaia la finestra
  DELAY PROCESS(Current process;60)
      ` scrivo il numero per l’attesa
      ` e se la stringa è vuota non entra
  For ($i;1;Length($attesa_stringa))
    POST KEY(Ascii($attesa_stringa[[$i]]);0;1)
    DELAY PROCESS(Current process;10)
  End for
      ` passiamo dopo
  POST KEY(9;0;1)
  DELAY PROCESS(Current process;10)
      ` se richiesto, spuntiamo WaiUser
  If ($B_WaitingUserDisconn)
    POST KEY(US ASCII code ;0;1)
    DELAY PROCESS(Current process;10)
  End if
      ` Conferma ed esce
  POST KEY(13;0;1)
End case
Codice Codice Cercare una stringa all'interno di un BLOB
Ecco un metodo "brutale" per cercare una stringa all'interno di un BLOB. Il metodo riceve come parametri un puntatore al BLOB, la stringa da cercare e la posizione da cui partire con la ricerca, e ritorna la posizione, altrimenti -1.


C_LONGINT($0;$3;$Start)
C_POINTER($1)
C_TEXT($2)
C_BOOLEAN($match)
$0:=-1
$Start:=$3
TEXT TO BLOB($2;$blob;Text without length )
$l:=BLOB size($1->)-1
$ll:=BLOB size($blob)-1
For ($i;$Start;$l)
    If ($1->{$i}=$blob{0})
        $match:=True
        For ($ii;0;$ll)
           If ($1->{$i+$ii}#$blob{$ii})
           $match:=False
           $ii:=$l
           End if
        End for
    End if
    If ($match)
        $0:=$i
        $i:=$l
    End if
End for


Fonte: 4DToday - Patrick Marty
Codice Codice Conoscere lo stato della base dati da remoto
Supponiamo di voler conoscere lo stato di una base dati remota, alla quale non possiamo accedere direttamente. Il seguente metodo, usando semplicemente il comando SEND PACKET, permette di costruire un file testo separato da tabulazioni (quindi apribile ad esempio con Excel) sullo stato della base dati da qualsiasi client. Nel'esempio che riporto vogliamo conoscere lo stato dei data segment e il numero di record delle tabelle.

$DocRef:=Create document("")

If (ok=1)
      `Intestazioni
      $Text:="Segment Size"+Char(Tab )+"Percorso"+Char(Carriage return )+Char(Carriage return )
      SEND PACKET($DocRef;$Text)
      `trovo i segmenti

      ARRAY TEXT(text_Segments;0)
      DATA SEGMENT LIST(text_Segments)

      `scrivo per ogni segmento dimensione del file e percorso

      For ($Segment;1;Size of array(text_Segments))
           $Size:=Get document size(text_Segments{$Segment})
           $Text:=String($Size;"#,###,###,##0")+Char(Tab )+text_Segments{$Segment}+Char(Carriage return )
           SEND PACKET($DocRef;$Text)
      End for

      `una linea vuota

      $Text:=Char(Carriage return )
      SEND PACKET($DocRef;$Text)

      `ancora intestazioni...

      $Text:="Table #"+Char(Tab )+"Table Name"+Char(Tab )+"Records"+Char(Tab )+"Cancellati"+Char(Tab )+"Frag%"+Char(Carriage return )+Char(Carriage return )
      SEND PACKET($DocRef;$Text)

      `ciclo su ogni tabella

      For ($Table;1;Count tables)

           $Recs:=Records in table((Table($Table))->)
           $Max:=Sequence number((Table($Table))->)
           $Deletions:=($Max-1)-$Recs
           $Frag:=($Deletions/$Recs)*100  `Expressed as a percentage

           $Text:=String($Table)+Char(Tab )+Table name($Table)+Char(Tab )+String($Recs)+Char(Tab )+String($Deletions)+Char(Tab )+String($Frag)+Char(Carriage return )

           SEND PACKET($DocRef;$Text)

      End for

      CLOSE DOCUMENT($DocRef)

End if
Plugin Plugin Connettersi ad un database mySQL
Se avete la necessità di connettervi ad un database mySQL, esistono due plugin per 4th Dimension che potrebbero aiutarvi:

- VmySQL 1.0, rilasciato da Rose development e distribuito da e-Node.

- MySQL Plugin di Pluggers Software.

Entrambi supportano mySQL 4.1 e funzionano a partire dalla versione 2003 del sistema di sviluppo. MySQL Plugin sotto Windows lavora anche con le versioni 6.7 e 6.8.

Codice Codice Trovare il valore di un attributo di un elemento XML usandone il nome
Analizzando il DOM di un XML può essere necessario ottenere l'attributo per un certo insieme di elementi: il problema è che un certo attributo può non essere presente in tutti gli elementi. Ad esempio:


  Modica
  Ragusa


Se si usa il comando GET ATTRIBUTE BY NAME e l'attributo non esiste, verrà restituito un errore. Può essere allora interessante usare GET ATTRIBUTE BY INDEX per evitare di ottenere l'errore:


` Parametri
    ` $1 - il nodo dell'elemento per cui trovare l'attributo
    ` $2 - il nome dell'attributo che stiamo cercando
    ` $0 - il valore dell'attributo o stringa vuota se non trovato
    ` ----------------------------------------------------
C_TEXT($1;$2)

C_TEXT($elemRef)
$elemRef:=$1

C_TEXT($searchName)
$searchName:=$2

C_LONGINT($i;$attribCount)
C_TEXT($attribName;$attribValue)
$attribCount:=Count XML attributes($elemRef)

  ` cerca l'attributo finché non viene trovato
For ($i;1;$attribCount)
  GET XML ATTRIBUTE BY INDEX($elemRef;$i;$attribName;$attribValue)
  If ($attribName=$searchName)
      ` esco dal ciclo
      $i:=$attribCount+3
  End if
End for

  ` ritorna attribValue (che potrebbe anche essere vuoto)
$0:=$attribValue


Plugin Plugin QPix: un'introduzione *
Ci sono fondamentalmente due possibilità per gestire le immagini in 4D. La
prima è quella di usare i comandi del linguaggio standard, la seconda di
usare un plug-in esterno. QPix è il plug-in della Escape per il trattamento delle immagini nei database 4th
Dimension.
Utilizzando la tecnologia di QuickTime, QPix permette, ad esempio:
- di acquisire informazioni su un'immagine;
- caricarla da un file per visualizzarla in un'area QPix oppure in una
variabile o in un campo;
- filtrarla e comprimerla;
- crearne una thumbnail di qualità normale o alta;
- acquisirla da una fonte TWAIN, creando volendo anche dei TIFF multipagina;
- esportarla su file in uno dei formati supportati da QuickTime.

1
Tecniche Tecniche Teoria della normalizzazione: Forma Normale di Boyce-Codd [BCNF]
La Forma Normale di Boyce-Codd (Boyce-Codd Normal Form, BCNF) è sicuramente la più forte fra le forme normali, anche per questo quindi molto difficilmente realizzabile. Infatti, in maniera più limitante rispetto alla 3NF:

Uno schema R è in BCNF se ogni volta che in R vale X -> A e A non è X, allora X è una superchiave per lo schema (cioè è una chiave o contiene una chiave).

Questa condizione è realmente molto complessa da ottenere, ma non impossibile. E’ infatti possibile scomporre uno schema relazionale in uno in BCNF, anche se non è garantito che conservi le dipendenze funzionali di partenza.

Ad esempio, lo schema Corso (C), Insegnante (T), Ora (H), Aula (R), Studente (S), e Voto (G), che indichiamo con CTHRSG, dove
C -> T ogni corso ha un insegnante
HR -> C in ogni istante un solo corso in un’aula
HT -> R un insegnante non gode del dono dell’ubiquità
CS -> G uno studente ha un solo voto per ogni corso
HS -> R anche gli studenti non godono del dono dell’ubiquità

Si può applicare un algoritmo che scompone il seguente schema in uno in BCNF, formato da HRS, CT, CSG, CHS.

Consulta da questo link l’indice delle faq sulla normalizzazione
Tecniche Tecniche Teoria della normalizzazione: Terza Forma Normale [3NF]
La Terza Forma Normale (3NF) aggiunge un ulteriore livello di controllo sull’unione delle tabelle. Una definizione l’abbiamo già vista in una delle FAQ precedenti:

Una tabella deve avere un campo che identifica in maniera univoca ognuno dei suoi record, e ogni campo deve descrivere il soggetto che la tabella rappresenta (Michael J. Hernandez, Database Design for Mere Mortals).

Equivalentemente:

Per verificare se una tabella in 2NF è anche in 3NF basta chiederci: “Esiste un campo non chiave che dipenda funzionalmente da un altro campo non chiave?”.

In maniera più formale:
Uno schema R è in 3NF se ogni volta che in R vale X -> A e A non è X (dove X è un insieme di campi e A è un campo specifico), allora o X è una superchiave per lo schema oppure A è primario (cioè è una delle chiavi, visto che in uno schema possono eserci più chiavi).


L’esempio visto per i CAP esplica perfettamente questi concetti.

Altro esempio “postale” è quello classico della tabella contenente Citta (che indicheremo con C), Via (S) e CAP (Z), dove valgono:
CS -> Z
Z -> C
Questo schema è in Terza Forma Normale.

E’ sempre possibile modificare uno schema in maniera tale che diventi in 3NF conservando le dipendenze funzionali di partenza.

Consulta da questo link l’indice delle faq sulla normalizzazione
Tecniche Tecniche Teoria della normalizzazione: Seconda Forma Normale [2NF]
La Seconda Forma Normale aiuta a identificare i casi in cui due tabelle distinte siano state erroneamente riunite in un’unica tabella. Si dice infatti che:

Una relazione è in Seconda Forma Normale (2NF) se e solo se è in 1NF e ogni attributo che non sia chiave è funzionalmente dipendente dalla chiave primaria.

In altre parole, una tabella è in 2NF se non ci sono gruppi ripetuti e ogni campo non facente parte della chiave è in dipendenza funzionale rispetto alla chiave stessa.

Una tabella che ha una sola chiave ed è in 1NF è già anche in 2NF.

Consulta da questo link l’indice delle faq sulla normalizzazione
Tecniche Tecniche Teoria della normalizzazione: Prima Forma Normale [1NF]
I principi della normalizzazione sono descritti in una serie di definizioni dette “Forme Normali”. La Prima Forma Normale (1NF) è la più semplice da soddisfare, la seconda è più complessa, e così via fino ad arrivare alla quinta o sesta forma normale. La Prima Forma Normale dice che:

Una tabella è in Prima Forma Normale se non contiene gruppi ripetuti.

Cos’è un gruppo e cosa può esserci di sbagliato? Quando si ha più di un campo che memorizza lo stesso tipo di informazione in una singola tabella, si ha un gruppo ripetuto. I gruppi ripetuti sono inevitabili quando il sistema di memorizzazione è un foglio di calcolo (che è una forma di database “piatto”), ma sono impensabili in un database relazionale. Ecco un esempio:

[Clienti]
ID_Cliente
Nome
Telefono1
Telefono2
Telefono3

Dov’è l’errore? Ecco, cosa succede quando ci serve un quarto numero di telefono? Siamo costretti ad aggiungere un campo, modificare tutti i form e riscrivere i metodi. Diventa problematico creare query a campi incrociati tra il cliente e i suoi contatti telefonici. Tutte le difficoltà descritte vengono risolte muovendo i telefoni in una tabella a parte:

[Clienti]
ID_Cliente
Nome

[Telefoni]
ID_Cliente (che permette la relazione con Clienti)
ID_Contatto (la chiave)
Telefono

Consulta da questo link l’indice delle faq sulla normalizzazione
Codice Codice Totali nelle liste di Output
Per mostrare una somma alla fine di una selezione in un normale layout mostrato con una Display selection non basta scrivere

:(Form event= On Header)
v_TotRow:=Sum([Fatture]Euro_tot)

ma se la display selection è suscettibile di cambiamenti a causa di
query, use set ecc bisogna scrivere nel metodo della Form

:(Form event= On Header)
v_TotRow:=Sum([Fatture]Euro_tot)
Set Visible(v_TotRow;True)

così il valore si aggiorna ad ogni cambio di record visibili.
Tecniche Tecniche Teoria della normalizzazione: ridondanze e storicizzazione dei dati
La necessità di storicizzare le informazioni, molto spesso si scontra, apparentemente, con la necessità di normalizzare un database.
Illustro il caso con due tabelle perfettamente normalizzate:

[Cliente]
Codice
RagioneSociale
...

[Fattura]
Numero
Data
CodiceCliente
...

Bene, cosa succede se il cliente in una certa data comunica una variazione di ragione sociale? Tutte le fatture antecedenti alla data di variazione non potrebbero più essere ristampate correttamente.
Per prevenire un problema di questo tipo, normalmente si ricorre all'aggiunta della ragione sociale del cliente anche sulla tabella [Fatture]:

[Fattura]
Numero
Data
CodiceCliente
RagioneSocialeCliente
...

In questo caso la ragione sociale memorizzata sulla fattura non è un dato ridondante ma un dato storico (storicizzato).

L'aspetto interessante, però, è che esiste un'altra via per risolvere il problema, anche se molto più complessa e dispendiosa sotto tutti i punti di vista.
Immaginiamo di voler gestire, per il cliente, anche il cambio di indirizzo di fatturazione (indirizzo, cap, città, provincia...).
Seguendo l'esempio di cui sopra, dovremmo aggiungere anche questi campi alla fattura, oppure, e qui sta l'alternativa, possiamo creare un secondo record cliente, accessibile con data:

[Cliente]
Codice
DataInizioValidità
RagioneSociale
....

Nella tabella [Cliente] è come se il cliente venisse "fotografato" ad una certa data.
Il campo DataInizioValidià viene a far parte, in un certo modo della chiave primaria.
Il cliente continua ad avere un codice solo ma i record sono multipli in base alla data di ricerca (esempio la data della fattura) (primo record con data validità inferiore a quella della fattura...)

Volendo si può sofisticare ancora il metodo, separando i dati storicizzabili in una tabella separata:
[Cliente]
Codice
DatoNonModificabile
....

[ClienteDatiStorici]
Cliente
DataInizioValidità
RagioneSociale
IndirizzoFatturazione
....

Questa tecnica non permette di creare delle relazioni efficienti in 4D perché si otterrebbero delle relazioni n-n. L'accesso al record deve avvenire con una query, un ordinamento decrescente sulla data inizio validità e una first record. Inoltre il presupposto è che tutte le tabelle interessate da questa logica abbiano una data di riferimento da utilizzare per gli accessi (nell'esempio la data della fattura per accedere al cliente).
Ripeto, il metodo è molto complesso, ma in alcune realtà è applicabile.

Consulta da questo link l’indice delle faq sulla normalizzazione
Tecniche Tecniche Teoria della normalizzazione: denormalizzare
Abbiamo visto ed elencato tutti i vantaggi legati alla normalizzazione. Allora perché in molti casi si ricorre volontariamente alla denormalizzazione, cioè alla deliberata violazione dei criteri di normalizzazione? Solitamente questo dipende dall’analisi del caso specifico. Facciamo un esempio banale.

Perché memorizziamo di ogni fattura il totale quando abbiamo già calcolato Imponibile ed IVA? Anzi di più, perché memorizzare Imponibile, IVA e Totale della fattura, se possiamo ottenere questi dati in ogni momento a partire dalle righe della fattura?

Questi esempi retorici fanno capire come nella realtà l’uso dei campi calcolati o delle tabelle di generazione delle statistiche o dei campi costruiti “ad hoc” per effettuare degli ordinamenti veloci (ordinare su un solo campo “riunito” può essere anche 40 volte più veloce dell’effettuare lo stesso ordinamento su due campi separati), malgrado possano essere considerati violazioni alle forme normali in realtà risultano necessari per migliorare le prestazioni sulle operazioni più complesse che un sistema di database deve compiere.

Consulta da questo link l’indice delle faq sulla normalizzazione
Tecniche Tecniche Teoria della normalizzazione: pro e contro
Ci si potrebbe chiedere: ma quali sono alla fine i vantaggi concreti nell’applicare un discorso così teorico? Eccoli elencati.

- Struttura del database assai più efficiente, flessibile e facile da mantenere.
- Migliore comprensione dei dati.
- Minore quantità di errori in itinere.
- Avvicinamento al mondo reale.
- Eliminazione dei dati duplicati
- Creazione di tabelle distinte solo quando realmente necessario

Certo, un contro c’è e non è da poco: l’uso della normalizzazione impone che la costruzione del database deve avvenire necessariamente DOPO essere venuti a conoscenza di tutte le esigenze dell’utente
Comunque i pro sembrano essere più dei contro...

Consulta da questo link l’indice delle faq sulla normalizzazione
Tecniche Tecniche Teoria della normalizzazione: progettare o implementare?
“Progettare un database” o “implementare un database” sono due attività completamente differenti.

Quando progettiamo una struttura, ciò viene fatto in maniera assolutamente indipendente dallo specifico tool di sviluppo che verrà poi usato per l’implementazione: questa, infatti, verrà fatta in un secondo momento.

Solo dopo aver disegnato la struttura in maniera astratta, passeremo all’implementazione vera e propria nel sistema preferito, 4D nel nostro caso.

Troppo spesso avviene che soprattutto i programmatori di database alle prime armi uniscano in un unico processo progettazione ed implementazione: ciò è anche dovuto al fatto che 4th Dimension offre una tentazione forte verso questo tipo di pratica, data la facilità di “disegnazione” (mi venga concesso il neologismo) dello Structure Editor. Ma avere a priori le idee chiare su “cosa” disegnare, permette di realizzare poi tutto in minor tempo e con ridottissimi margini di errore.

Consulta da questo link l’indice delle faq sulla normalizzazione
Tecniche Tecniche Teoria della normalizzazione: l'abbiamo sempre usata
Il solo termine “teoria” potrebbe far pensare a qualcosa di estremamente complesso e teorico: niente di più sbagliato: la teoria della normalizzazione si potrebbe sintetizzare nella seguente affermazione:

Una tabella deve avere un campo che identifica in maniera univoca ognuno dei suoi record, e ogni campo deve descrivere il soggetto che la tabella rappresenta (Michael J. Hernandez, Database Design for Mere Mortals).

Questa affermazione è interessante: ogni record deve essere identificabile in modo unico e ogni campo deve riguardare l’oggetto in questione. In maniera più semplicistica possiamo dire allora che:

La normalizzazione ha come obiettivo l’eliminazione dei campi ridondanti e l’unificazione di tabelle.

E’ pressoché certo che un principio simile lo abbiamo bene o male, e più o meno coscientemente, già tenuto in considerazione sviluppando i nostri database!

Consulta da questo link l’indice delle faq sulla normalizzazione
Tecniche Tecniche Teoria della normalizzazione
Analizzando le statistiche di accesso a Sviluppo4D mi sono accorto con sorpresa, e ovviamente con piacere, del fatto che molti dei nuovi visitatori del sito sono sviluppatori (quanto meno probabilmente) in cerca di informazioni e notizie relative a sistemi di sviluppo di database “altri” rispetto a 4th Dimension: oltre ai già ampiamente trattati Access e FileMaker, anche ricerche riguardanti Macromedia Cold Fusion, Oracle, Visual Basic, Code Warrior, AS 400, Delphi portano visitatori su questo sito. Visto dunque che 4D può diventare fonte di conoscenza “open source” anche per altre piattaforme, ho pensato di dedicare alcune faq ad un argomento che prescinde realmente dal tipo di sistema di sviluppo per database utilizzato dal programmatore-visitatore: la teoria della normalizzazione.

Tanto per dare un’idea di ciò che cercherò di sviluppare più ampiamente possibile nelle varie faq sull’argomento, normalizzazione è un processo, in parole molto povere, che determina quali campi debbano appartenere ad una determinata tabella in un database relazionale. Sono poche, semplici ed intuitive regole che portano vari benefici:
- Eliminazione delle ridondanze (cioè la duplicazione dei dati);
- Creazione di modelli “chiusi” (per la teoria degli insiemi) di entità del mondo reale, con i loro processi e le loro relazioni;
- Strutturazione dei dati assai flessibile.

La normalizzazione assicura lo sfruttamento di tutti i benefici dati dall’uso di un sistema di database relazionale: il tempo “sprecato” in questo processo viene ripagato da sé immediatamente.

Come fonte per i vari argomenti trattati userò un brillante articolo di David Adams e Dan Beckett (e le relative fonti) del 1997, ricavato da Programming 4th Dimension: The Ultimate Guide degli stessi autori, oltre a “Basi di dati e basi di Conoscenza” di Jeffrey D. Ullman

Consulta da questo link l’indice delle faq sulla normalizzazione
Plugin Plugin Un esempio di uso di 4D Open
Per vedere in azione alcuni comandi di 4D Open, il plug-in che permette a due programmi 4D (di cui almeno uno 4D Server) di comunicare, facciamo il seguente esempio.

Supponiamo di conoscere il nome della tabella in cui cercare un certo campo ma di non conoscere il nome del campo stesso. L'unica cosa che sappiamo di questo campo è che è l'unico campo boolean della tabella. Vediamo di ottenere la posizione di questo campo nella tabella.

Supponimo che la tabella sia la numero 1. Avremo:

C_LONGINT($File;$ErrCode;$Field)
ARRAY LONGINT(aTypes;0) `tipi
ARRAY LONGINT(aLength;0) `… lunghezza (degli alfanumerici)
ARRAY LONGINT(aIndexes;0) `indicizzato?
ARRAY LONGINT(aInvisible;0) `invisibile?
ARRAY STRING(15;aNames;0) `nomi

$File:=1 `tabella numero 1

$ErrCode:=OP Get field properties (vl_ConnectID;$File;$Invisible;aNames;aTypes;aLength;aIndexes;aInvisible)

For ($i;1;Size of Array(aTypes))
  If (aTypes{$i}=Is Boolean)
    $Field:=$i 'questo è il numero
  End if
End for

Si noti che l'array aTypes è stato dichiarato longint, ma poteva essere string text real o integer. Il comando ritorna il valore corretto in base al tipo di array inserito.
Codice Codice Ricerca di elementi negli array bidimensionali
Come ben sappiamo, il comando Find in array ritorna il primo elemento di un array che corrisponde a un certo valore. Per gli array bidimensionali il processo è lievemente differente.
Supponiamo di avere:

Array Text(Myarray;2;2)
Myarray{1}{1}:="Cinema"
Myarray{1}{2}:="Teatro"
Myarray{2}{1}:="Film"
Myarray{2}{2}:="Commedia"

Se cerchiamo la parola Cinema 4D ci restituisce:

$find:=Find in array(Myarray{1};"Cinema")
$find1:=Find in array(Myarray{2};"Cinema")
$find sarà 1
$find1 sarà -1

Per effettuare una ricerca diretta su tutto l'array allora si scriverà:

$arrSize:=Size of array(Myarray)
For ($i;1;$arrSize)
  $find:=Find in array(Myarray{$i};"Cinema")
    If ($find>0)
      `qualsiasi cosa
  End if
End for
Codice Codice Controllo delle strutture bloccate usando le password
4th Dimension permette di usare un database da CD: in questo caso, giustamente, la struttura risulta bloccata. In generale però è bene controllare se la struttura è bloccata o meno: se via 4D Client modifico una struttura bloccata, le modifiche saranno scritte in cache e uscendo da 4D Server andranno perse: il caso tipico è la copia della struttura da CD a disco fisso (in ambiente Windows).

Un modo per controllare se una strutura è bloccata può essere quello di modificare le password della struttura via codice: per eseguire questa operazione la struttura non deve risultare bloccata, altrimenti viene generato un errore -9937. Nel database method "On Startup" verrà lanciato questo processo:

$ref:=New process("MyLockProc";32*1024)

Il metodo lanciato avrà questa forma:

ON ERR CALL("ControlloErrori")
DELAY PROCESS(Current Process;60)
GET USER PROPERTIES(2;name;startup;pass;uses;dat)
$error:=Set user properties(2;name;startup;pass;uses;dat)
ON ERR CALL("")

Nella procedura ControlloErrori posso visualizzare messaggi di errore.

IMPORTANTE: il metodo di controllo deve partire su un nuovo processo e il DELAY PROCESS iniziale deve essere di almeno 60 tick.
Plugin Plugin Creare tabelle e campi da programma
Nella versione 2004.1 di 4D è disponibile con 4D Pack il comando:

AP Add table and fields (tableName; fieldNamesArray; fieldTypesArray; fieldLengthsArray{; listFormTemplate{; detailFormTemplate}})

Il comando è stato fondamentalmente creato per creare "al volo" le strutture. Prende come parametri il nome della tabella (fino a 31 caratteri), un array con i nomi dei campi, un array con i tipi, un array con la lunghezza relativa per i campi alfanumerici, e, opzionalmente, il nome dei template da usare per i layout di output e di input.
Codice Codice Ricerca su tutti i campi testuali di una Tabella
  ` Ricerca di una stringa su tutti i campi alfanumerici e testo di una tabella
    ` _________________________________________________________________
    ` $1 = Puntatore alla tabella
    ` _________________________________________________________________

C_POINTER($1)
C_STRING(80;$Alfa)
C_POINTER($PtrField;$PtrTable)
C_INTEGER($FieldNum;$TableNum;$NumeroMinimoCaratteri)

$PtrTable:=$1
$NumeroMinimoCaratteri:=3

$Alfa:=Request("Cerca...: (min. "+String($NumeroMinimoCaratteri)+" car.)")
If ((ok=1) & ($Alfa#""))
If (Length($Alfa)>=$NumeroMinimoCaratteri)
$TableNum:=Table($PtrTable)
$FieldNum:=Count fields($PtrTable)
For ($i;1;$FieldNum)
$PtrField:=Field($TableNum;$i)
If ((Type($PtrField->)=Is Alpha Field ) | (Type($PtrField->)=Is Text ))
QUERY($PtrTable->;$PtrField->=$Alfa;*)
$i:=$FieldNum+100
End if
End for

  ` Verifica che abbia eseguito la query precedente
If ($i>($FieldNum+10))
For ($i;1;$FieldNum)
$PtrField:=Field($TableNum;$i)
If ((Type($PtrField->)=Is Alpha Field ) | (Type($PtrField->)=Is Text ))
QUERY($PtrTable->; | $PtrField->="@"+$Alfa+"@";*)
End if
End for
QUERY($PtrTable->)
Else
Alert ("Assenza campi alfanumerici in tabella")
End if
Else
Alert ("Devi introdurre almeno "+String($NumeroMinimoCaratteri)+" caratteri")
End if
End if
    ` _________________________________________________________________
Comandi Comandi Controllare tutti i plugin caricati
Con la versione 2004 di 4D è possibile conoscere la lista di tutti i plug-in caricati utilizzando il comando GET PLUGIN LIST: un ottimo sistema per controllare allo startup del database se tutti i componenti necessari al programma sono presenti.

GET PLUGIN LIST elenca tutti i plugin, sia quelli integrati (tipo 4D Chart) che quelli di terze parti.
Info Info Read only automatico
4th Dimension mette in automatico una tabella in modalità Read-only quando vengono usati comandi che non richiedono accesso in scrittura alla tabella stessa. Questi comandi sono:

  DISPLAY SELECTION
  DISTINCT VALUES
  EXPORT DIF
  EXPORT SYLK
  EXPORT TEXT
  GRAPH TABLE
  PRINT SELECTION
  PRINT LABEL
  QR REPORT
  SELECTION TO ARRAY
  SELECTION RANGE TO ARRAY

È possibile controllare lo stato in lettura/scrittura di una tabella usando il comando Read only state
Tecniche Tecniche Assegnare un data file ad una struttura specifica con WEDD
Capita spesso di sviluppare più applicazioni che girano sulla stessa macchina. Ovviamente può provocare una quantità di danni incredibile l'apertura di un file dati con una struttura diversa da quella prevista!
4D (fino alla 2003) permette di associare in maniera esclusiva un file dati e una struttura attraverso il Customizer Plus.
Questa applicazione permette infatti di associare una risorsa WEDD sia alla struttura che al file dati, in maniera da limitare l'uso di un certo data file alla sola struttura associata. Infatti, solo la struttura con una medesima risorsa WEDD potrà aprire quel file dati, prevenendo così i danni accidentali provocati da aperture di file dati errate.

Ecco come fare:
1) aprire la struttura col Customizer Plus;
2) doppio clic sull'ucona WEDD;
3) nella dialog, inserire un nome per il parametro WEDD;
4) chiudere la dialog, chiudere il file e, alla richiesta, salvare le modifiche;
5) ripetere la stessa operazione sul file dati, avendo cura di inserire lo stesso valore WEDD della struttura.

A questo punto, quando verranno aperti un file dati e una struttura con WEDD differenti, verrà segnalato l'errore: "The structure and the data files do not correspond to each other. The data file cannot be opened with this structure".
Per eliminare l'impostazione di una risorsa WEDD basta aprire la base dati o la struttura con 4D Customizer Plus e fare doppio clic sull'icona WEDD premendo sulla tastiera Alt (Option su Mac).

Con 4D 2004, WEDD invece può essere configurato direttamente dalle preferenze del database (percorso da passare a OPEN 4D PREFERENCES è "/Database/Data Management/WEDD")
Codice Codice Controllare lo stato di un processo senza process ID
Il comando Process state ritorna lo stato di un processo il cui numero viene passato come parametro. In realtà è possibile controllare lo stato di un processo anche quando non se ne conosca il numero, ma solo il nome. Il seguente frammento di codice lo dimostra:

C_LONGINT($0)
C_STRING(255;$1)
$0:=Process state(Process number($1))

Passando come parametro il nome del processo, il comando Process number ne ritorna il numero. Questo numero viene così poi passato a Process state.

Tecniche Tecniche Istanze multiple di 4D WebSTAR V
Ci sono te modi per lanciare WebSTAR V:
1) lanciare l'applicazione manualmente;
2) inserirla nelle applicazioni di avvio per l'utente;
3) durante l'installazione, scegliere l'opzione di installazione di WebSTAR come "Startup Item".

Questi ultimi due casi sono notevolmente differenti: nel caso 2, infatti, WebSTAR viene lanciato quando l'utente relativo accede ala macchina, mentre nel caso 3 WebSTAR viene lanciato all'avvio per qualsiasi utente, per di più in backgroud (il che significa che la finestra di WebSTAR Launcher risulta invisibile).

Potrebbe causare dunque problemi la possibilità di eseguire più volte WebSTAR, "abilitando" sia il caso 2 che il caso 3.

Un modo per vedere se ci sono più WebSTAR lanciati può essere quella di lanciare il Visualizzatore di Processi di MacOSX e controllare i processi "WS" in esecuzione: l'unico processo che dovrebbe essere in esecuzione più volte è WSAdminServer, ma comunque non più di due istanze.

Nel caso di duplicazione, rimuovere WebSTAR o dagli elementi di login dell'utente o dal lancio come servizio utilizzando l'Installer.
Plugin Plugin Backup di 4d Monoutenza *
4dBackup è un plugin forinito con 4d che di solito si usa in versione client-server. E' possibile però usarlo anche in monoutenza, ma o lo si esegue in ambiente User o occorre comandarlo da procedura. Ecco un esempio di utilizzo:

C_INTEGER ($Progress_i;$Fill_i)
If (BK Begin full backup = 0)
      If (BK Start copy = 0)
           Repeat
              BK GET PROGRESS ($Progress_i; $Fill_i)
              MESSAGE ("Backup in corso: " + String ($Progress_i) + "%")
           Until (BK Get state # 4)
      End if
      BK END BACKUP
End if


1
Tecniche Tecniche Le variabili dei componenti
Quando si scrive un componente è importante ricordarsi che verrà utilizzato all'interno di una struttura con altri metodi e variabili. Quindi bisogna seguire queste importanti regole :
1) il nome deve essere unico: in genere puoi aggiungere un prefisso usato anche per dare il nome ai metodi, ad esempio "ESE_Dup_" per un componente della società Esempio per contare i duplicati.
2) usa sempre un metodo Privato che inizi con il prefisso "Compiler_" per fare in modo che chi usa il componente non abbia problemi a compilare correttamente la propria struttura, quindi "Compiler_ESE_Dup_VariabiliProcesso"
3) le variabili non devono essere utilizzate al di fuori del componente, per cui è necessario creare dei metodi per leggere/scrivere il valore (chiamati "accessor method"). Ad esempio, se nel componente ci fosse la variabile <>ESE_QuantiDoppi_i si potrebbero scrivere due metodi ESE_Dup_Leggi_QuantiDoppi e ESE_Dup_Scrivi_QuantiDoppi. Oppure un metodo unico, a cui puoi passare un parametro se vuoi impostare la variabile, comunque ritorna sempre il valore della stessa.

  ` Metodo ESE_Dup_QuantiDoppi(Numero) --> Numero
  ` Accesso: Protetto
C_LONGINT($0;$1)
If (count parameters>=1)
  If ($1>0)
    <>ESE_QuantiDoppi_i:=$1
  Else
    ALERT("Il valore passato a "+Current method name+" deve essere positivo")
  End if
End if
$0:=<>ESE_QuantiDoppi_i

Estratto dalla sessione "Write Better Code with 4D Components" di Dave Batton presentata alla "4D Summit Conferences 2004"
Codice Codice Uscita corretta da 4D Server con Stored procedure
Una stored procedure è un processo lanciato sulla macchina server.

L'utilizzo di questa tecnica è molto spesso indicato perché limita la comunicazione via network fra client e server.
Una stored procedure può essere lanciata:

- da 4D Server, usando il comando New process;

- da 4D Client, usando il comando Execute on server.

Vediamo di usarla per eseguire una corretta uscita da 4D Server.
Nel database method "On Server Startup" inseriamo questo codice:

<>StopServer:=False
<>StoredProc:=New process("Server_Method";32*1024;"StoredProc (server)")

Il metodo "Server_Method" non fa altro che un ciclo molto semplice, che dura finché <>StopServer non diventa "Vero"

Repeat
        $currentTime:=Current time
        
        DELAY PROCESS(Current process;60)
Until (<>StopServer=True)


Ora, esistono due modi, come ben sappiamo di uscire da 4D Server:
- "Disconnect from server in nn min." è sicuramente il più pericoloso: trascorso il numero di minuti TUTTI I PROCESSI VERRANNO CHIUSI, con grosso rischio, ovviamente di integrità del database;
- "Wait for all Users to disconnect" attende che tutti i client vengano chiusi.

Se viene usato questo secondo sistema di chiusura del server, è possibile usare la variabile <>StopServer impostata precedentemente per controllare nelle varie stored procedures se il server sta o meno per chiudersi.

Per fare in modo che 4D Server attenda la conclusione dei vari processi si può usare questo codice nel database method "On Server Shutdown" (dobbiamo controllare tutti i processi possibili del server, quindi anche Web):

  <>StopServer:=True
      
`inizializzo

      $Index:=1
      $nbUsersProcs:=5
      $serverReady:=11
      
      Repeat
          
           ` Quanti processi vivi?
           $nbProcess:=Count user processes
          
           ` Condizione dei processi kernel
           $interface:=ProcessExist (204;1) ` process 'User interface'
           $client:=ProcessExist (204;2) ` process 'Client manager'
           $cache:=ProcessExist (204;3) ` process 'Cache manager'
           $web:=ProcessExist (401;1) ` process 'Web server'
           $index:=ProcessExist (204;4) ` process 'Index manager'
          
           ` Una variabile che condensa gli stati
           $serverReady:=$interface+$client+$cache+$web+$index
          
           ` Delay per 5 secondi
           DELAY PROCESS(Current process;300)
          
      Until (<>StopServer=True) & ($index=0) & ($nbProcess<5) & ($serverReady<11)


Il metodo "ProcessExist" controlla lo stato dei processi kernel accedendo alle risorse:

  ` $1 => STR# number
      ` $2 => STR# item number
      ` $0 <= number

      $0:=Process number(Get indexed string($1;$2))


Così, se $index è uguale a zero, significa che nessun processo di indicizzazione è in corso e il server può uscire.

Un altro approccio potrebbe essere quello di far lanciare un processo al client che controlli lo stato di <>StopServer, in modo che possa completare tutti i suoi processi prima che venga eseguito il comando "Quit 4D"
Info Info Numeri Integer e overflow automatico
Il tipo Integer in 4D può avere valori fra -32.768 e 32.767. Se viene inserito un valore maggiore o minore di questi limiti, il numero sarà modificato automaticamente per rientrarci: questa operazione si chiama "overflow".
Per esempio se aggiungi 1 ad un intero 32.767 esso diventerà -32768. Così se togli 1 da -32.768 otterrai 32.767.

Però bisogna anche sapere che in 4D (dalla versione 3 in poi) in realtà sono interi veri i campi e gli array. In realtà le varibili sono dei Long integer (che hanno un limite molto più alto).

Per verificare la cosa, prova ad eseguire questo codice controllando nel debugger i valori ottenuti:

CREATE RECORD([Tabella])
[Tabella]Intero:=32766
[Tabella]Intero:=[Tabella]Intero+1 ` 32,767 = normale
[Tabella]Intero:=[Tabella]Intero+1 ` -32,768 = overflow
[Tabella]Intero:=[Tabella]Intero-1 ` 32,767 = overflow ancora!

ARRAY INTEGER(arrayInteger;1)
arrayInteger{1}:=32766
arrayInteger{1}:=arrayInteger{1}+1 ` 32,767 = normale
arrayInteger{1}:=arrayInteger{1}+1 ` -32,768 = overflow

C_INTEGER(variabile)
variabile:=32766
variabile:=variabile+1 ` 32,767 = normale
variabile:=variabile+1 ` 32,768 = non c'è l'overflow!!!!

If (Type(variabile)=Is LongInt ) `infatti se controllo... è un Long integer
  ALERT("variabile è di tipo long integer.")
End if
Tecniche Tecniche La precedenza nelle espressioni aritmetiche
Il sistema secondo cui vengono valutate delle espressioni algebriche viene chiamato precedenza.
4D valuta le espressioni non secondo la normale precedenza matematica (potenza, moltiplicazione-divisione, somma-sottrazione), ma molto più semplicemente leggendo da sinistra a destra. Ecco perché:

3 + 4 * 5

restituisce come risultato 35, mentre secondo la notazione comune il risultato dovrebbe essere 23. Quindi, per valutare l’espressione secondo la notazione classica è neccessario utilizzare la parentesizzazione:

3 + ( 4 * 5)

con cui 4d restituisce appunto 23.

Un sistema di valutazione di questo genere può sembrare assai incomprensibile, ma d’altro canto esistono molti modelli matematici che preferiscono "letture" che non usino le precedenze o che permettano valutazioni dirette "sinistra - destra". Il caso più famoso è probabilmente quello della forma postfissa (detta anche polacca, dalla nazionalità di Lucasievic, che l'ha formalizzata), in cui gli operatori non si trovano in mezzo (come nella notazione infissa, che è quella da noi comunemente utilizzata) ma dopo gli operandi (4*3 diventa 4 3 *). Tale forma ha il vantaggio di non aver bisogno di parentesizzazione per specificare la precedenza fra gli operatori.

Fonte: Alfredo Ferro - Teoria ed Applicazione delle Macchine Calcolatrici - Università degli Studi di Catania, 1991-2
Tecniche Tecniche Posso convertire un real in longint?
Nell uso di un interprete, due aspetti da separare sono gli errori dovuti a una cattiva "interpretazione" di una istruzione e quelli dovuti ad un modo sbagliato di usare l'interprete stesso (errori di programmazione). Andrew S. Tanenbaum ne "I moderni sistemi operativi" afferma che "...in molti casi la conversione tra i tipi è necessaria o utile...": tutto ciò che il computer usa è formato da una serie di 0 e 1, basta semplicemente "tradurre" l'insieme di bit da String a Num o da real a longint, nei modi previsti dall'interprete.
Il manuale del Language di 4th Dimension afferma che:
“You can assign any Number data type to another; 4D does the conversion, truncating or
rounding if necessary”.

Ciò significa in buona sostanza che convertire un numero con virgola in intero (il cosiddetto casting delle variabili) è ammissibile, solo che 4D non fornisce garanzie sul tipo di metodo che verrà utilizzato per effettuare tale conversione: potrebbe essere indifferentemente usato il troncamento come l’arrotondamento.

Fonti:
A.S. Tanenbaum: Modern Operating Systems, 1994
Domenico Cantone: Linguaggi Formali e Compilatori, Università degli Studi di Catania, 1996

Comandi Comandi Confrontare due reali: il comando SET REAL COMPARISON LEVEL
Il comando SET REAL COMPARISON LEVEL a cui viene passato come parametro un numero, indica il valore epsilon secondo cui valutare l'uguaglianza fra due numeri reali.

Come visto nella faq sull'arrotondamento dei numeri reali, il computer effettua delle approssimazioni per calcolare i numeri reali: quindi anche nel calcolare l'uguaglianza fra due numeri 4th Dimension deve tenere in considerazione questa approssimazione, controllando se se la differenza fra due numeri eccede o meno un certo valore. Questo valore è dato proprio dall'epsilon passato come parametro a SET REAL COMPARISON LEVEL.

Vediamo un esempio.
Dati due numeri reali a e b, se Abs(a-b) è maggiore di epsilon i numeri verranno considerati diversi, altrimenti uguali.
Di default, 4D usa come epsilon 10 elevato a -6 (10^-6). Ciò significa che:

• 0,00001=0,00002 restituisce False perché la loro differenza (0,00001) è maggiore di 10^-6.
• 0,000001=0,000002 ritorna True perché la loro differenza (0,000001) non è maggiore di 10^-6.
• 0,000001=0,000003 restituisce False perché la loro differenza (0,000002) è maggiore di 10^-6.

Bisogna dunque usare SET REAL COMPARISON LEVEL se risulta necessario confrontare valori più bassi di 10^-6.

Si noti inoltre che se si deve eseguire una query o un ordinamento su un campo contenente valori più bassi di 10^-6, il comando SET REAL COMPARISON LEVEL deve essere usato prima della costruzione dell'indice.

Il comando non ha alcun effetto sul calcolo o la visualizzazione dei numeri reali.
Tecniche Tecniche Gli arrotondamenti dei numeri reali in 4D
L’argomento dell’arrotondamento dei numeri reali è da sempre uno dei più dibattuti fra gli sviluppatori 4D. Molti considerano questo aspetto come un baco del sistema di sviluppo, altri semplicemente un problema con cui convivere serenamente avendo solo qualche accortezza. Vediamo il problema.

A scuola ci insegnano che il risultato di 1/3 è 0,3333….. con 3 periodico. Ma concettualemte sappiamo anche che tre volte un terzo fa uno. Il problema è che il computer non conosce questo problema e ritiene corretto calcolare esattamente queste espressioni. Ad esempio, a seconda del computer che usiamo, vedremo che 1/3 è calcolato con un numero ben preciso di “3” dopo la virgola: questo numero è chiamato “precisione della macchina”.

Su Mac 68K, la precisione è 19; ciò significa che 1/3 è calcolato con 19 cifre significative.
Su Win e su Power Mac, questo numero è 15; così 1/3 è visualizzato con 15 cifre significative.
Se visualizziamo l'espressione 1/3 nella finestra del Debugger di 4th Dimension, otterremo 0,3333333333333333333 su un 68K e qualcosa come 0,3333333333333333148 su Windows o su Power Macintosh. Si noti che le ultime tre cifre sono differenti perché la precisione su Windows e su Power Macintosh è minore rispetto al 68K. Tuttavia, se si visualizza l'espressione (1/3)*3, il risultato è 1 su entrambe le macchine.

Perchè il valore 1/3 sembra differente?
Nei Macintosh 68K, il sistema operativo usa 10 byte (80 bit) per memorizzare numeri reali, mentre Windows Power Macintosh 8 byte (64 bit). Ecco perchè i numeri reali hanno fino a 19 cifre significative su 68K e fino a 15 cifre significative Windows su Power Macintosh.

Allora perchè l'espressione (1/3)*3 restituisce 1 su tutte e tre le macchine?

Un computer può fare soltanto i calcoli approssimati. Di conseguenza, mentre confronta o calcola numeri, un computer non tratta i numeri reali come oggetti matematici ma come valori approssimati. Nel nostro esempio, 0,3333... moltiplicato per 3 dà 0,9999...; la differenza fra 0,9999... e 1 è così piccola che la macchina considera il risultato uguale a 1 e conseguentemente restituisce 1. Per i particolari riguardanti il "quanto piccola può essere la differenza fra 0,9999... e 1" vi rimandiamo ad una FAQ sul comando SET REAL COMPARISON LEVEL.

Bisogna dunque distinguere due cose sui numeri reali in 4D:

Come vengono calcolati e confrontati
Come vengono visualizzati sullo schermo o sulla stampante

Originariamente, 4D gestiva i numeri reali usando il tipo di memorizzazione a 10-byte. Per compatibilità, le versioni Power Macintosh e Windows di 4D continuano ancora a usare il tipo di dati a 10-byte. Poiché l'aritmetica in virgola mobile è effettuata su Windows o su Power Macintosh usando 8 byte, 4D converte i valori da 10 byte a 8 byte e viceversa. Di conseguenza, se carico un record che contiene valori reali (salvati su 68K Macintosh) su Windows o su Power Macintosh, è possibile perdere una certa precisione (19 - 15 cifre significative). Tuttavia, nel caso diametralmente opposto, su un 68K non ci sarà perdita di precisione.

Usando il Customizer Plus, si può regolare il numero di cifre da non considerare quando vengono mostrati dei numeri reali su 68K o su Power Macintosh e Windows. Le impostazioni di default sono: nessuna cifra su 68K e cinque cifre su Power Macintosh e Windows.
  
Plugin Plugin Stampare da codice un'area 4D Write [2]
Quando si vuole stampare un'area 4D Write si può usare il comando WR PRINT. Tale comando però stampa l'area solo una volta. Per stampare un'area un numero di volte pari al numero di record selezionati di una tabella bisogna usare il comando WR PRINT MERGE.
Inoltre WR PRINT MERGE permette di impostare in maniera parametrica la visualizzazione o meno della dialog di stampa (0 non la mostra, 1 si), visto che la sintassi è:

WR PRINT MERGE (area; tabella; mostra_dialog)

Ecco un esempio su come stampare una copia di una lettera per ogni cliente:

ALL RECORDS (Clienti]) `Tutti i clienti
QUERY ([Lettere];[Lettere]Ref="Spedisci") `Carica un template
Temp:=WR New offscreen area `Crea l'offscreen area
WR PICTURE TO AREA(Temp;[Letters]Doc_) `Il template nella offscreen area
WR PRINT MERGE (Temp;3) `Unisce il template con i dati nella tabella 3
WR DELETE OFFSCREEN AREA (Temp) `Cancella l'offscreen area
Info Info Le icone identificative dei campi
Le icone che si trovano accanto ai campi delle tabelle nella Structure window sono rintracciabili usando i numeri di pict a partire da 150, ad esempio:

- 150 restituisce l'icona del campo di tipo string;
- 151 restituisce l'icona del campo di tipo real;
- 152 restituisce l'icona del campo di tipo text;
- 158 restituisce l'icona del campo di tipo int;
- 159 restituisce l'icona del campo di tipo longint.


1
Plugin Plugin Stampare da codice un'area 4D Write [1]
Per stampare un'area di 4D Write senza l'intervento dell'utente è necessario utilizzare il comando WR PRINT. Questo comando equivale alla scelta dell'utente di Print dal menu File dell'area.

E' interessante notare, per chi inserisce campi dinamici nelle aree 4D Write, che il comando WR PRINT può stampare sia i valori che i riferimenti, ma questi ultimi non vengono calcolati automaticamente: l'aggiornamento dei valori deve essere forzato prima dell'esecuzione della stampa. Ad esempio:

WR EXECUTE COMMAND (area;wr cmd compute references)
WR PRINT (area; 0;1)
Plugin Plugin Ottenere testo formattato da 4D Write
Ecco un metodo che ritorna come risultato un blob contenente il testo formattato presente in un'area 4DWrite. Il risultato del metodo puo essere inserito in un altro documento usando il comando WR INSERT STYLED TEXT.

    ` $1 - L'area 4D Write
    ` $2 - Posizione del primo carattere
    ` $3 - Posizione dell'ultimo carattere
    ` $0 - Blob il testo

C_LONGINT($1;$wrarea)
C_LONGINT($2;$3;$begsel;$endsel)
C_BLOB($0;$StyledTextBlob)

$wrarea:=$1
$begsel:=$2
$endsel:=$3

Case of
: (($begsel>=0) & ($endsel>0))
    If ($begsel<$endsel)
        WR SET FRAME($wrarea;wr text frame )
        WR SET SELECTION($wrarea;$begsel;$endsel)
        $StyledTextBlob:=WR Get styled text($wrarea)
    End if
: (($begsel=-1) & ($endsel=-1)) ` Copy all
    WR SET FRAME ($wrarea;wr text frame )
    WR EXECUTE COMMAND ($wrarea;wr cmd select all )
$StyledTextBlob:=WR Get styled text ($wrarea)

End case

$0:=$StyledTextBlob

Codice Codice Comporre i numeri telefonici da 4D
Posto di avere un modem collegato alla porta seriale del computer, ecco un metodo che utilizza i comandi seriali di 4D e i comandi AT del modem per comporre un numero di telefono.

$numero_t:=$1
SET CHANNEL(MacOS Serial Port+Protocol XONXOFF;Speed 1200+Data bits 8+Stop bits One+Parity None)
SEND PACKET("+++") `Reinizializza il modem
DELAY PROCESS(Current process;120) `Pausa di 2 secondi
$messaggio:="AT S7=0 M1 DT"+$numero_t +Char(13)
SEND PACKET($messaggio)
DELAY PROCESS(Current process;120) `Pausa di 2 secondi
SET CHANNEL(11) `Chiude la porta seriale
Codice Codice Scaricare con il client il programma 4D su cui siamo connessi
Procedura da lanciare sul server

CREATE RECORD([ARCHIVI])
[ARCHIVI]NomeArchivio:="NOME STRUTTURA DEL PROGRAMMA"

$NuovoArchivio:=[ARCHIVI]NomeArchivio+"2"
vErrore:=0
ok:=1

ON ERR CALL("ErroreSuBlob")
$TipoArchivio:=Document type([ARCHIVI]NomeArchivio)
If (($TipoArchivio#"") & (ok=1))
    COPY DOCUMENT([ARCHIVI]NomeArchivio;$NuovoArchivio;*)
    If (ok=1)
        $Ref:=Open document($NuovoArchivio)
        If (ok=1)
           CLOSE DOCUMENT($Ref)
           [ARCHIVI]TipoArchivio:=Document type(Document)
           If (ok=1)
           DOCUMENT TO BLOB(Document;[ARCHIVI]DataFork)
           If (ok=1)
           COMPRESS BLOB([ARCHIVI]DataFork)
           End if
           If (ok=1)
           DOCUMENT TO BLOB(Document;[ARCHIVI]ResourceFork;*)
           If (ok=1)
           COMPRESS BLOB([ARCHIVI]ResourceFork)
           End if
           End if
           End if
        End if
        
        DELETE DOCUMENT($NuovoArchivio)
    End if
End if
ON ERR CALL("")
[ARCHIVI]DataModifica:=Current date(*)
[ARCHIVI]OraModifica:=Current time(*)
[ARCHIVI]BytesDataFork:=BLOB size([ARCHIVI]DataFork)
[ARCHIVI]BytesResourceFork:=BLOB size([ARCHIVI]ResourceFork)
SAVE RECORD([ARCHIVI])

UNLOAD RECORD([ARCHIVI])

procedura da lanciare sul client (anche in remoto) dopo il tempo necessario al completamento della prima da parte del server

$NuovoArchivio:=[ARCHIVI]NomeArchivio+" copia"
$Ref:=Create document($NuovoArchivio;[ARCHIVI]TipoArchivio)
If (ok=1)
    CLOSE DOCUMENT($Ref)
    EXPAND BLOB([ARCHIVI]DataFork)
    EXPAND BLOB([ARCHIVI]ResourceFork)
    BLOB TO DOCUMENT($NuovoArchivio;[ARCHIVI]DataFork)
    BLOB TO DOCUMENT($NuovoArchivio;[ARCHIVI]ResourceFork;*)
End if

su mac osx la struttura viene memorizzata dentro il pacchetto di 4D
Codice Codice Campi booleani come testo: un altro approccio
Abbiamo visto in una faq sui Quick Report come trasformare un campo booleano in testo.

Un sistema altrettanto interessante di risolvere il problema potrebbe essere utilizzare i formati associabili al comando String. Se volgio infatti Mostrare il testo "Vero" oppure "Falso" a seconda del valore di un campo booleano posso scrivere:

$0:=String(Num($1);"Vero;;Falso")
Stile Stile Uso di @ nei comandi Object Properties
I comandi della categoria Object Properties (FONT, FONT SIZE, FONT STYLE, ENABLE BUTTON, DISABLE BUTTON, BUTTON TEXT, SET CHOICE LIST, SET ENTERABLE, SET VISIBLE, SET FORMAT, SET FILTER, SET COLOR, SET RGB COLORS, GET OBJECT RECT, MOVE OBJECT, BEST OBJECT SIZE, Get alignment, SET ALIGNMENT) agiscono sulle proprietà degli oggetti presenti in un form durante la sua visualizzazione in modalità User o Custom (le proprietà impostate vengono ovviamente perse alla chiusura del form).

Questi comandi accettano come parametro sia il nome dell'oggetto che il nome relativo (sia che esso rappresenti un campo o una variabile), con una sintassi del tipo:

COMANDO({*;} oggetto{ ; parametri specifici del comando})

Se viene usato il parametro *, questo indica che in "oggetto" viene specificato l'object name (e non la corrispondente variabile). Essendo questo una stringa, posso utilizzare "@" nel nome per selezionare gruppi di oggetti. Quindi:

"IlMioPulsanteBtn" indicherà il pulsante con quel nome.
"@Btn" indicherà tutti gli oggetti il cui nome finisce per "Btn"
Web Web Un javascript per i valori di default
Se sviluppo siti internet che gesticono form di input ho due possibilità di gestire i valori di default.
Uno è quello di riempire i campi col valore che mi attendo più frequentemente (ad esempio per il campo nazione potrebbe essere Italia) senza però essere certo che l'utente, in fase di inserimento, non abbia sbagliato a non modificare quel valore.

Il secondo è assai più raffinato, e prevede l'uso di uno script in java per controllare dinamicamente l'inserimento dei dati, prima che questi vengano poi processati e controllati da 4D al "Submit" del form. Per il nostro esempio riguardante la nazione:

<input
type="text"
name="newcountry"
size="30"
maxlength="40"
value="«Inserisci la nazione»"
onFocus="If (this.value=='«Inserisci la nazione»'){this.value=''}" onBlur="if(this.value==''){this.value='«Inserisci la nazione»'}">

In questo modo, nell'evento onFocus, se l'oggetto ha ancora il valore di default, lo cancello per permettere l'inserimento.
Se non viene inserito nulla, inserisco nuovamente il valore di default.


Fonte: 4DToday
Codice Codice Usare i tasti freccia per scorrere la lista di record
Sarà capitato a tutti di ricevere da un cliente la richiesta di poter utilizzare le frecce per poter scorrere le liste di record nel form di output di una tabella.

Per chi non usa la 2004 ecco un metodo da associare a quattro pulsanti non visibili nel form di output. Ad ognuno dei pulsanti collegare la pressione dei pulsanti "Freccia Giù" "Freccia Su" "Pag Giù" "Pag Su".

Il metodo, prendendo come parametri la tabella corrente, l'azione e opzionalmente il numero di record da saltare selezionerà il record successivo/precedente o salterà avanti/indietro di "$3" record:

DEFAULT TABLE($1->)
C_LONGINT($N;selectedRecord)
$N:=Records in selection($1->)
$pTable:=$1
BOOLEAN ARRAY FROM SET($set_ab)
$numeroRecord_l:=Find in array($set_ab;True;0)
LONGINT ARRAY FROM SELECTION($pTable->;$record_al)
selectedRecord:=Find in array($record_al;$numeroRecord_l)

If (Count parameters>2)
  $quanti:=$3
Else
  $quanti:=1
End if

Case of
  : ($2="down")
    selectedRecord:=selectedRecord+$quanti
    If (selectedRecord>$N)
      selectedRecord:=$N
    End if

  : ($2="up")
    selectedRecord:=selectedRecord-$quanti
    If (selectedRecord<1)
      selectedRecord:=1
    End if

End case

GOTO SELECTED RECORD($1->;selectedRecord)

CREATE EMPTY SET($1->;"ListSet")
ADD TO SET($1->;"ListSet")
HIGHLIGHT RECORDS("ListSet")
CLEAR SET("ListSet")

Usando lo stesso principio si potrebbero aggiungere anche i pulsanti per selezionare il primo o l'ultimo record della lista.
Web Web Scrivere i robots per i siti dinamici in 4D [3/3]
In aggiunta all'uso del meta tag ROBOTS che abbiamo visto si può inoltre utilizzare un secondo meta tag il "revisit-after". La sintassi è del tipo:

<META NAME="revisit-after" content="nnnn days">

dove nnnn indica il numero di giorni che il motore di ricerca deve attendere per rivisitare la pagina. Ora, essendo il nostro sito completamente dinamico, fare rivisitare tutte le pagine non avrebbe senso, il motore di ricerca penserebbe che le pagine del sito non sono permanenti e lo declasserebbe.

La soluzione diventa allora inserire sulla prima pagina del sito un valore di nnnn molto basso, ad esempio 2, per fare in modo che la prima pagina venga rivisitata spesso (e con essa, di conseguenza tutto il sito, poiché sulla prima pagina abbiamo scritto il nostro robot "index,follow"), e in tutte le altre un valore altissimo, tipo 1000 o 2000, per evitare che queste vengano rivisitate con richiesta diretta da parte del motore di ricerca.
Web Web Scrivere i robots per i siti dinamici in 4D [2/3]
Supponiamo di avere un sito che raccolga link e materiale su alcuni degli argomenti informatici più ricercati su Internet: computers, database software, database design, Oracle, Access, 4Th Dimension, ecc...

Date le parole chiave, per essere da motore di ricerca mi interessa che la prima pagina e ogni pagina di dettaglio sia indicizzata, ma nelle pagine successive ci sono ancora link uguali a quelli della prima pagina: senza i Robots il sito verrebbe visitato dal motore di ricerca all'infinito.
Invece decidiamo di usarli! La sintassi è:

<META NAME =ROBOTS CONTENT="all | none | index | noindex | follow | nofollow">

Alcuni esempi:

<meta name="robots" content="index,follow">
<meta name="robots" content="noindex,follow">
<meta name="robots" content="index,nofollow">
<meta name="robots" content="noindex,nofollow">

dove:
- follow o nofollow indicano allo snake di visitare o no tutti i link della pagina.
- index o noindex indicano allo snake di indicizzare o meno la pagina.

La soluzione diventa semplice: alle pagine in testa all'albero di visita darò un meta tag di tipo "index,follow", a quelli che contengono notizie o risorse assignerò un "index,nofollow".
Web Web Scrivere i robots per i siti dinamici in 4D [1/3]
Come richiesto ad Umberto Migliore durante il corso web spiego come abbiamo risolto per Sviluppo4D.it il problema delle visite dei motori di ricerca.

Il motore di ricerca, per creare l'indice delle pagine lancia un programma detto "snake" che, a partire dalla pagina iniziale, si preoccupa di visitare tutti i link del sito.
Molti programmatori avranno però notato che gli snake di Google o Msn, nel cercare di tracciare tutto il sito dinamico costruito con 4D, in realtà continuano a girare sempre sulle stesse pagine, innalzando così il numero di richieste in maniera esagerata: quando Sviluppo4D conteneva ancora poche informazioni ho notato picchi di 250 visite contemporanee!

Questo è dovuto al fatto che, come avrà notato il programmatore che si è guardato il sorgente del sito, per riconoscere gli utenti, 4D genera una "Session", diversa per ogni visita (guardate l'URL che avete in questo momento sulla barra dell'indirizzo). Questa sessione però, in breve lasso di tempo, viene cancellata, il che significa che alla prossima visita del motore di ricerca, quella pagina per lo snake non esisterà più, e ricomincerà da capo a visitare il sito.
La soluzione adottata per problema è stata semplice ed efficace: utilizzare i meta tag "ROBOTS".
Info Info Utilizzare strutture o file dati Mac su NTFS
Supponiamo di avere un database creato su Mac (con i due file MioProgetto e MioProgetto.data) e volerlo utilizzare su Windows.

Posto che la partizione Windows deve essere NTFS, è necessario intanto creare una copia compressa sia della struttura che del file di dati (ciò previene che la parte delle risorse non venga danneggiata o addirittura non venga copiata, se il programma che utilizziamo per copiare i file non supporta le risorse).

Portati questi file compressi sulla partizione NTFS, scompattarli e chiamare la struttura MioProgetto.4DB e il file dati MioProgetto.4DD

Nella versione 2004 4D divide anche su Mac i file in 4DB e RSR (per le strutture), 4DD e 4DR (per le base dati).
Info Info Codici di errore: Network errors
Ecco un elenco degli errori che possono verificarsi nelle connessioni di rete:

-10001 La connessione corrente al database è stata interrotta.
-10002 La connessione per questo processo è stata interrotta.
-10003 Parametri di connesione non corretti.
-10020 Il server scelto con Select 4D server non è stato trovato.
-10021 Il server scelto con Find 4D server non è stato trovato.
-10030 E' avvenuta una desincronizzazione durante il ciclo di scrittura.
-10031 E' avvenuta una desincronizzazione durante il ciclo di lettura.
-10033 Dimensione dei dati errata durante il ciclo di lettura.
-10050 Opzione sconosciuta in Get/SetOption.
-10051 Valore errato in Get/SetOption.
Info Info Codici di errore: OS File Manager Errors
Ecoo un elenco degli errori ritornati dal file manager del sistema oprativo, solitamente generati quando si usano i comandi della sezione System Documents:

-33 La cartella per il documento è piena. Non puoi creare un nuovo documento sul disco.
-34 Il disco è pieno. Non c'e altro spazio disponibile sul disco.
-35 Il volume specificato non esiste.
-36 Errore di I/O . Esiste probabilmente un blocco danneggiato sul disco.
-37 Nome del documento o del volume errato.
-38 Tentativo di leggere o scrivere un file non aperto.
-39 Richiesto un end-of-file logico durante l'operazione di lettura.
-40 Tentativo di accedere ad una posizione precedente all'inizio del file.
-41 Memoria insufficiente per aprire un nuovo documento sul disco.
-42 Troppi documenti aperti sullo stesso disco.
-43 Documento non trovato.
-44 Il volume è bloccato da una impostazione hardware.
-45 Il documento è bloccato.
-46 Il volume è bloccato da una applicazione.
-47 Tentativo di accedere ad un documento che è stato cancellato.
-48 Tentativo di rinominare un documento col nome di un documento cancellato.
-49 Tentativo di aprire un file già aperto in modalità di scrittura.
-51 Tentativo di accedere ad un documento con un document reference number non valido.
-52 Errore interno del file manager (posizione del file marker perduta).
-53 Volume non in linea.
-54 Tentativo di aprire un file bloccato per la scrittura.
-57 Tentativo di lavorare con un disco non-Macintosh.
-58 Errore nel file system esterno.
-60 Errore nel master directory block. Il disco è danneggiato.
-61 I permessi Read/write non consentono la scrittura.
-64 Problema hardware sul disco (cattiva installazione, formattazione non corretta...)
-84 Problema hardware sul disco (cattiva installazione, formattazione non corretta...)
-120 Tentativo di accedere ad un documento usando il percorso di una cartella che non esiste.
-121 Il percorso di accesso non può essere creato.
-124 Tentativo di accedere ad un volume condiviso disconnesso.
Novita' Novita' 4D 2004: il pulsante Done di default
Una caratteristica delle versioni precedenti di 4th Dimension era l'aggiunta in automatico di un pulsante di chiusura del form ("Done") nel caso in cui il form stesso non contenesse altri pulsanti.

A partire dalla versione 2004 questo pulsante non viene più aggiunto automaticamente: quindi l'unico modo per chiudere la finestra diventa quello di utilizzare la combinazione di uscita impostata ("Escape"), ad esempio su Windos Ctrl+"." e su Mac Command+"."
Tecniche Tecniche Se 4D Tools non riesce a riparare il file dati
Se il tentativo di riparare un file di dati con 4D Tools non va a buon fine, è possibile che il resource file (il file nomefiledati.4DR su Windows, dove nomefiledati è il nome del file .4DD) sia danneggiato. In tal caso si può provare a sostituirlo. Ecco come fare per le due piattaforme:

Windows
- Fare un backup del database e del datafile.
- Creare un nuovo database: in questo modo abbiamo, oltre al vecchio data file, anche un nuovo file di dati.
- Sostituire il vecchio file .4DR associato al vecchio file dati col file .4DR creato dal nuovo database.
- A questo punto abbiamo il vecchio file dati associato ad un nuovo file .4DR.
- Provare ad eseguire adesso la riparazione con 4D Tools.

Mac
- Fare un backup del database e del datafile.
- Creare un nuovo database: in questo modo abbiamo, oltre al vecchio data file, anche un nuovo file di dati.
- Per entrambi i file dati, usare 4D Transporter per portare i dati in formato Windows.
- Sostituire il file .4DR associato al vecchio file dati col file .4DR associato al database creato poco prima.
- A questo punto abbiamo il vecchio file dati associato ad un nuovo file .4DR.
- Essendo il tutto in formato Windows, utilizzare 4D Transporter nuovamente per portare la combinazione vecchio_file_dati-nuovo_4DR in formato Mac.
- Provare ad eseguire adesso la riparazione con 4D Tools.
Info Info Windows XP Service Pack 2 e 4th Dimension
L'arrivo di Windows XP Service Pack 2 comporta vantaggi e svantaggi. Un vantaggio è che il problema dovuto al fix KB824141 sulle vecchie versioni non sembra più presente.

Lo svantaggio è che le porte per 4D Server devono essere controllate per poter pubblicare i database col firewall attivo (standard del client è 19813, standard del web è la 80)
Web Web Usare i metodi con 4DIF
Supponiamo di avere un metodo che ritorni una stringa, e di volerlo utilizzare in un tag 4DIF per controllare se il metodo ritorna una stringa vuota. Penseremmo di scrivere:

<!--4DIf (Il_metodo="")-->

Pur essendo Il_metodo un prokect method corretto che ritorna una stringa, 4D ritornerà un errore del tipo:

:A Boolean argument was expected

Il problema è che bisogna aiutare 4th Dimension a valutare l'espressione, e lo si fa aggiungendo una coppia di parentesi alla chiamata del metodo, in questo modo:

<!--4DIf (Il_metodo()="")-->

Ciò segnala a 4D che Il_metodo è un metodo.
Comandi Comandi Uso di Delay Process o di Pause Process
I comandi Delay Process e Pause Process fanno essenzialmente la stessa cosa: interrompono l'esecuzione del processo indicato. La differenza fondamentale sta nel fatto che:

- DELAY PROCESS ferma l'esecuzione del processo per un certo lasso di tempo, dopo il quale il processo riprende automaticamente;

- PAUSE PROCESS ferma l'esecuzione del processo fino a che non viene eseguito sullo stesso processo un comando RESUME PROCESS.

Una situazione tipica si ha ad esempio quando un metodo lancia un nuovo processo e deve attendere il completamento dell'esecuzione di quest'ultimo per poter continuare. L'algoritmo per il metodo chiamante sarebbe:

$id:=New process
While (Process state($id)>=0)
    DELAY PROCESS (Current process; tempo_di_pausa_che_penso_ragionevole)
End while

In questo caso l'attesa e la ripresa sono gestite dal processo chiamante. Il vantaggio è che il processo chiamato non ha interazione alcuna col processo chiamante. Lo svantaggio è che il ciclo While potrebbe sprecare troppo tempo macchina (se il tempo di attesa è troppo corto) o fare restare il processo inattivo inutilmente (se il tempo di attesa è alto). Vantaggi e svantaggi si invertono se l'algoritmo è:

$id:=New process
PAUSE PROCESS (Current process)

Il processo resta fermo in attesa che il processo chiamato non lo svegli con un RESUME PROCESS.
Web Web La sintassi di 4DIF
La documentazione di 4th Dimension specifica che la sintassi di 4DIF richiede l'uso delle parentesi, quindi:

<!--4DIf (qualsiasivariabile=True)-->

4DIF potrebbe essere usato anche senza parentesi, ma meglio usarle, anche perché, ad esempio, il plug-in per Dreamweaver richiede l'uso delle parentesi perché il tag venga correttamente valutato.

Inoltre è bene sottolineare che, insieme alle parentesi, la sintassi richiede uno spazio tra il tag e la parentesi; infatti se si usa:

<!--#4DIF(qualsiasivariabile=True)-->

4D non valuterà correttamente il 4DIF.

Novita' Novita' I cambiamenti del form event On Display Detail
Fino alla versione 2003, l'evento On Display Detail veniva eseguito per i soli record visualizzati, non per tutto il form: per capirci, se ho un form può contenere 15 record ma da mostrare ne ha solo 4, il codice inserito per l'evento On Display Detail verrà eseguito solo 4 volte.
Invece, a partire dalla versione 2004, On Display Detail si comporta diversamente: infatti, nell'esempio visto, il codice verrebbe eseguito comunque 15 volte, anche se i record mostrati sono solo 4.

Ecco una esemplificativa modifica ad una porzione di codice del manuale di upgrade alla 2004. Si noti anche l'uso del comando Displayed line number, che ritorna il numero di riga visualizzata:

If (Form event=On Display Detail)
    If (Displayed line number %2=0) 'riga pari
        If (Record number([Tabella1])>-1) 'esiste un record sulla riga
        'testo nero su verde chiaro
           SET RGB COLORS([Tabella1]Campo1;0;0x009FFF89)
        Else 'il record non esiste
           'testo nero su azzurro
           SET RGB COLORS([Tabella1]Campo1;0;0x00A1FFFF)
        End if
    Else 'riga dispari
        If (Record number([Tabella1])>-1)
           'nero su verde
           SET RGB COLORS([Tabella1]Campo1;0;0xAA99)
        Else
           'blu scuro
           SET RGB COLORS([Tabella1]Campo1;0;0x70FF)
        End if
    End if
End if

Le righe contenenti un record sono o verde scuro o verde chiaro, le righe senza record sono azzurre o blu.
Web Web Nidificazione dei tag 4D e HTML
Spesso può risultare necessario usare i tag 4D all'interno di altri tag HTML.
Analizziamo questo esempio:

<input type ="text" name="varName" value = "<!--#4DVAR [Table]Field-->">

Si noti l'uso del tag 4D all'interno delle virgolette. Ciò non crea problemi.
Infatti, le specifiche HTML impediscono l'uso di tag nidificati. Se scriviamo:

<input type="checkbox" name="cbOne" value="Yes" <!--#4dhtmlvar isChecked-->>

l'editor HTML (che sia Dreamweaver o GoLive) potrebbe ritornare un warning o un errore. Per aggirare l'ostacolo siamo costretti a riscrivere il codice così:

<!--#4DIF(isChecked="CHECKED")-->
<input type="checkbox" name="cbOne" value="Yes" CHECKED>
<!--#4DElse-->
<input type="checkbox" name="cbOne" value="Yes">
<!--#4DENDIF-->
Codice Codice Impostare i valori di default
Nelle form di input è possibile prevedere che un campo sia già compilato con la data o l'ora corrente, o con il successivo Sequence Number.

Nelle proprietà dell'oggetto Campo, nella riga Default Value è possibile scrivere:

#H per l'ora corrente

#D per la data corrente

#N inserisce il sequence number o numero progressivo automatico
Web Web Quando viene eseguito il database method On Web Authentication
Se usato, il metodo On Web Authentication viene richiamato nelle seguenti circostanze:

- quando 4D riceve un URL che comincia con 4DACTION/

- quando 4D riceve un URL che comincia con 4DMETHOD/

- quando 4D riceve un URL che comincia con 4DCGI/

- quando 4D riceve la richiesta per una pagina statica che non esiste

- quando 4D processa una richiesta con un tag 4DSCRIPT in una pagina semidinamica

- quando 4D processa una richiesta con un tag 4DLOOP seguito da un metodo in una pagina semidinamica (solo la prima chiamata )

- quando 4D riceve una richiesta SOAP

Web Web Seleziona un elemento in una lista Html
Ecco il testo con i tag da scrivere in Html per far sì che in una lista sia selezionato un elemento particolare.

Il codice sfrutta un generico Array di testo che contiene le varie opzioni e il contenuto in una variabile "sceltaProposta"

<SELECT NAME="Lista">
<!--4DLOOP Array-->
<!--4DIf (sceltaProposta=Array{Array})-->
<OPTION VALUE="<!--4DVAR Array{Array}-->" selected><!--4DVAR Array{Array}--></OPTION>
<!--4DElse-->
<OPTION VALUE="<!--4DVAR Array{Array}-->"><!--4DVAR Array{Array}--></OPTION>
<!--4DENDIF-->
<!--4DENDLOOP-->
</SELECT>
Tecniche Tecniche Mostrare in un testo una variabile o un campo
Se devo mostrare un testo che contenga un campo o una varibile in un layout, solitamente potrei scrivere:

vTestoT:="La variabile ha valore "+vAltraVariabile

Stessa cosa per il campo:

vTestoT:="Il campo Nome ha valore "+[Table]Nome

A questo punto inserisco la variabile vTestoT nel form. Una maniera certamente più rapida di risolvere la questione è inserire direttamente una zona di testo nel form e utilizzare i simboli "<" e ">" per segnalare la variabile o il campo. Basterà dunque scrivere:

La variabile ha valore <vAltraVariabile>

o

Il campo Nome ha valore <[Table]Nome>
Web Web Cambio dinamico della porta web
Il numero della porta TCP usata da 4D può essere modificato da linguaggio utilizzando il comando SET DATABASE PARAMETER utilizzando il parametro 15 (corrispondente alla costante Port ID). Per fare in modo che 4D usi la porta 8080 si userà:

SET DATABASE PARAMETER(15;8080)

o

SET DATABASE PARAMETER(Port ID;8080)

Comandi Comandi Controllare il blocco dei record con LOCKED ATTRIBUTES
Dopo aver controllato che un record è bloccato (prima della modifica o della cancellazione), con Locked, può essere usato il comando LOCKED ATTRIBUTES per capire chi o quale processo ha in uso il record.

La sintassi è:

LOCKED ATTRIBUTES ({table; }process; user; machine; processName)

dove "table" è la tabella (se specificata, altrimenti userà quella di default), mentre gli altri parametri sono variabili che restituiscono il numero di processo (in modalità client/server è il numero di processo del server), nome utente e macchina (ma solo se in modalità client/server, altrimenti si ottengono due stringhe vuote) e il nome del processo.
Macro Macro Metodo dei pulsanti *
Scrivendo un metodo per gli oggetti è possibile selezionare gli eventi nelle proprietà e scrivere il codice per gestire solo un particolare evento. Questo purtroppo non è molto leggibile, perche, ad esempio, in fase di debug potresti non sapere se e quale evento è selezionato quando tracci il codice.

E' quindi buona norma gestire gli eventi con il solito Case, anche per gestire il clic su un pulsante, che è l'evento più normale.
Per facilitare il compito di scrivere sempre la stessa cosa, ecco una macro che lo fa per conto nostro.



Case of
:(Form event=On clicked)

End case



Basta incollare questo codice XML nel file Macros.xml file, che si trova nella corrente cartella 4D di sistema, all'interno dei tag .

A questo punto è possibile usare all'interno del metodo la macro usando il menu contestuale (click-destro su windows o control-click su mac) e scegliendo la macro "OnClicked" dal menu "Insert Macro"

1
Info Info 4th Dimension, MS Access e FileMaker Pro [15] Criteri di scelta
Una domanda che ci si pone è: "Quale sistema scegliere?". Oppure: "Secondo quali criteri un sistema può essere preferibile ad un altro?".

La chiave attraverso la quale scegliere un sistema di sviluppo per database può essere l'equilibrio: l'equilibrio fra i bisogni dello sviluppatore e quelli dell'utente. Lo sviluppatore desidera un sistema semplice da sviluppare e flessibile. L'utente cerca un database con caratteristiche che lo rendano potente, ma di facile utilizzo e che costi poco.

FileMaker è davvero semplice, ma assai limitato: si arriva ai limiti dell'applicativo non appena si hanno delle richieste un po' complesse.
Access ha in alcuni aspetti grandi capacità, ma in altre risulta meno potente o assai complesso. In multiutenza ha un limite per numero di 4-5 utenti oltre il quale è necessario passare all'uso di un server SQL.
4th Dimension ha la facilità di sviluppo di FileMaker, ma possiede una grandissima varietà di caratteristiche aggiuntive rispetto agli agli altri due sistemi. Rispetto a FileMaker è più potente, rispetto ad Access è più facile e semplice da manutenere senza perdere in potenza.

Consulta da questo link l’indice delle faq su 4th Dimension, MS Access e FileMaker Pro

Web Web Riconoscere la lingua dell'utente
Il sistema più semplice e affidabile è quello di dare all'utente la lingua con cui lui si imposta il browser.. così accontenti anche l'utente che si trova in italia, ma parla (o meglio legge) inglese.

L'informazioni la trovi facilmente nell'header che ti invia il browser.

Ecco un esempio di codice:

GET HTTP HEADER(array_nomi;array_valori)
$elem:=find in array(array_nomi;"Accept-language")
$codice:=array_valori{$elem}

La variabile codice conterrà una stringa del tipo "it, us.." dove sono elencate le lingue in ordine di preferenza.
Codice Codice Convertire un numero di secondi in formato Ora
Ecco un modo semplice per convertire un valore LongInt che corrisponda al numero di secondi in un valore Time, Ora:

ora_h:=Time(Time string(46800)) ` come scrivere ora_h:=?13:00:00?

Nota che 86.400 secondi sono 24 ore. Se converti un numero che è più grande di 86.400, la funzione Time string aggiungerà ore, minuti e secondi.

Quindi, Time string (86401) ritornerà 24:00:01.
Web Web Modificare la barra di stato nei siti dinamici
Se devo creare (o programmare, o sviluppare, o mantenere) un sito dinamico con 4th Dimension, capita che i collegamenti all'interno della pagina non siano "diretti", ma siano in realtà dei "redirect" che 4D esegue al momento del clic, vedi ad esempio lo stesso webhome.

Ovviamente non è carino che il link venga visualizzato sulla barra di stato del browser con la sintassi del redirect di 4D.

Si può fare allora in modo che, al passare del mouse sul collegamento, la barra di stato visualizzi l'indirizzo che verrà poi realmente visitato. Basta usare nella zona dell'html con l'"a href" che segnala il link questo attributo:

onMouseOver="return stat('INDIRIZZO')"
Novizi Novizi Scorrere una selezione di record
Esistono tre modi di scorrere una selezione di record, a seconda che si usino i comandi For...End for, Repeat...Until o While...End while . Ecco il dettaglio delle sintassi:

For ($i;1;Records in selection([Table 1]))
  ` esegui le operazioni
  NEXT RECORD([Table 1])
End for


Repeat
  ` esegui le operazioni
  NEXT RECORD([Table 1])
Until (End selection([Table 1]))



While (Not(End selection([Table 1])))
  ` esegui le operazioni
  NEXT RECORD([Table 1])
End while


Potrebbe essere necessario aggiungere prima del ciclo un FIRST RECORD([Table 1])

Codice Codice Il percorso alla cartella Desktop
Ecco un metodo per avere un'informazione utile, ma non così semplice da ottenere.

C_TEXT($0;$percorso_t;$4Dfolder_t)
C_LONGINT($platform;$system;$vlSys;$i)
PLATFORM PROPERTIES($platform;$system)
If ($platform=Windows)
      $percorso_t:=System folder(Desktop Win )
Else
      $vlSys:=$system\256
      If ($vlSys=16)
           $percorso_t:=System folder(Preferences or Profiles_User )
           $percorso_t:=Replace string($percorso_t;"Library:Preferences:";"Desktop:")
      Else
           $4Dfolder_t:=System folder(System )
           For ($i;1;Length($4Dfolder_t))
              If ($4Dfolder_t[[$i]]=":")
                 $percorso_t:=Substring($4Dfolder_t;1;$i)+"Desktop Folder:"
                 $i:=Length($4Dfolder_t)+1
              End if
           End for
      End if
End if
$0:=$percorso_t
Comandi Comandi Il comando Get menu item
Il comando Get menu item riceve come parametri il numero identificativo del menu, il numero identificativo della voce del menu e, opzionale, il numero di processo e restituisce il testo di una voce di menu.
Plugin Plugin Il comando FTP_GetFileInfo
La sintassi è:
FTP_GetFileInfo (ftp_ID; hostPath; size; modDate)

Passando come primi due parametri un ID di sessione e il percorso ad un documento, il comando degli IC FTP_GetFileInfo restituisce nella variabile passata come terzo parametro la dimensione del documento e nella variabile passata come quarto parametro del documento la data di modifica. La funzione restituisce un error code.

L'ID della sessione viene ottenuto usando il comando FTP_Login.

Se come percorso viene passato un percorso completo, la Current Working Directory (CWD) della sessione ftp viene modificata con il percorso passato a FTP_GetFileInfo (stesso comportamento di FTP_GetDirList).
Info Info Ottenere il numero di menu e una sua voce in modo esadecimale
A partire dalla versione 6 si possono utilizzare gli operatori di confronto per ottenere il numero di menu scelto e il numero della relativa voce scelta in questo modo:

Menu := (Menu selected & 0xFFFF0000) >> 16
menu_command := Menu selected 0xFFFF
Codice Codice Usare un solo metodo per tutte le voci di menu
Piuttosto che creare un metodo per ogni voce di menu, si può pensare di scrivere un unico metodo "Menu_Manager" che gestisca tutte le possibili chiamate da menu. Questa tecnica risulta interessante perché permette di gestire le chiamate anche dinamicamente.

` Project Method: Menu_Manager

C_TEXT($SelectedMenuItem)
$SelectedMenuItem:=Get menu item(Menu selected\65536;Menu selected%65536)

Case of
: ($SelectedMenuItem="Nuovo Record")
` Chiama qui il Metodo per creare un record
: ($SelectedMenuItem="Record successivo")
` Chiama qui il Metodo per andare al record successivo
: ($SelectedMenuItem="Record precedente")
` Chiama qui il Metodo per andare al record precedente
: ($SelectedMenuItem="Stampa report")
` Chiama qui il Metodo per stampare un report
End case

Info Info Personalizzazione del Database Structure *
E' possibile modificare lo sfondo standard della finestra del database structure con una
immagine personalizzata.

IMPORTANTE: LA MODIFICA E' DEFINITIVA, nel senso
che non è possibile ritornare all'immagine di sfondo standard di 4D (quella
con gli ingranaggi, per intenderci).
Per effettuare la modifica:
- mettere negli appunti l'immagine che si vuole come sfondo;
- cliccare su un punto vuoto (senza tabelle o relazioni) della finestra
Database structure;
- incollare l'immagine.

1
Info Info La posizione della 4D folder, la cartella di sistema di 4th Dimension
Il percorso della cartella di sistema dove 4D posiziona le sue preferenze può essere ottenuto in due modi:
- usando il comando Get 4D folder;
- guardando la voce 4D folder nella pagina "Program" della dialog "About 4th Dimension".

A partire dalla versione 6.8:
On MacOS X:{Disk}:Library:Application Support:4D
Mac OS 9: Disk}:System folder:Application Support:4D
Windows NT: {Disk}:\{System folder}\Profiles\All Users\Application Data\4D
Windows 98/ME: {Disk}:\{System folder}\All users\Application Data\4D
Windows 2000/XP: {Disk}:\Documents and Settings\All Users\Application Data\4D
Windows 2000/XP con 4D Client:
{Disk}:\Documents and Settings\{Current user}\Application Data\4D
Macro Macro Macro: una per Open form window
Ecco un esempio di macro da aggiungere al file Macro.xml nella cartella 4D del sistema. La macro apre una dialog centrata, ma può ovviamente essere modificata per effettuare altre operazioni sulle finestre. Basta inserire questo codice in qualsiasi punto del file tra i tag <macros> ... </macros>.

<macro name="OpenDialog">
  <text>
    C_LONGINT($windowRef)
    $windowRef:=Open form window(<selection/>;Plain window;Horizontally centered;Vertically centered)
    DIALOG(<selection/>)
    CLOSE WINDOW($windowRef)
  </text>
</macro>

Ad esempio, scrivendo e selezionando il seguente testo:

[Table 1];"Form1"

ed eseguendo la macro, si otterrà:

C_LONGINT($windowRef)
$windowRef:=Open form window([Table 1];"Form1";Plain window;Horizontally Centered;Vertically Centered)
DIALOG([Table 1];"Form1")
CLOSE WINDOW($windowRef)
Info Info No more room to save the record: l'errore -9999
L'errore -9999, "No more room to save the record" segnala che tutti i segment del database sono pieni o non c'è più spazio per scrivere sul disco.

Tuttavia è molto più facile che questo errore sia in realtà dovuto al fatto che il file di dati è in modalità "locked" o sia bloccato il disco su cui il file si trova.

E' possibile controllare allo startup lo stato della base dati con il comando is data file locked.

Macro Macro Macro: Inserire un elemento alla fine di un array
Per chi non usa 4th Dimension 2004 e non può dunque usare APPEND TO ARRAY ma usa ancora la 2003, ecco un sistema alternativo per inserire rapidamente un elemento in coda ad un array: aprire il file Macros.xml (si trova nella cartella 4D di sistema, ad esempio su Win C:\WINDOWS\4D) e aggiungere prima del tag di chiusura </macros> il seguente codice:

<macro name="MioAppendToArray">
<text>
INSERT ELEMENT(<Selection/>;Size of Array(<Selection/>)+1)
<Selection/>{Size of Array(<Selection/>)}:=</text>
</macro>

Al solito supponiamo che sia mioarray l'array a cui vogliamo aggiungere un elemento in coda. Nel punto del metodo in cui ci serve fare l'inserimento dell'elemento scriviamo "mioarray" (senza virgolette) e lo selezioniamo. Tasto di destra sul testo selezionato e scegliamo "Insert Macros - MioAppendToArray". La macro genererà il seguente codice:

INSERT ELEMENT(mioarray;Size of array(mioarray)+1)
mioarray{Size of array(mioarray)}:=

Il cursore si troverà inoltre dopo il simbolo "=" pronto per farci inserire il valore desiderato.
Codice Codice Evitare la duplicazione dei processi
Se abbiamo l'accortezza (o usiamo il sistema) di dare ad ogni processo un nome che lo identifichi univocamente, siamo già pronti a controllare che uno stesso processo sia lanciato più volte (e dunque duplicato).
Basta usare infatti il parametro "*" del comando New process: se esiste già un processo con lo stesso nome di quello che stiamo creando, New process restituirà l'id del processo esistente, invece di crearne uno nuovo. Ecco dunque un'idea di metodo di apertura dei processi, che prende come parametri nome del metodo, dimensione dello stack e nome del processo:

C_TEXT($1;$3;$NomeDelMetodo_t;$NomeDelProcesso_t)
C_LONGINT($2;$StackSize_l;$procid_l)
$NomeDelMetodo_t:=$1
$StackSize_l:=$2
$NomeDelProcesso_t:=$3
$procid:=New process($NomeDelMetodo_t;$StackSize_l;$NomeDelProcesso_t;*)
BRING TO FRONT($procid_l)
Stile Stile Uso della ricorsione e metodi ricorsivi
Uno dei sistemi di programmazione più affascinanti è sicuramente dato dall'uso della ricorsione: lo scopo è fondamentalmente quello di scrivere meno codice possibile, facendo in modo che operazioni identiche siano ripetute in cascata sempre dallo stesso metodo: facciamo un esempio.
Supponiamo di voler scrivere un metodo che conti il numero di file presenti in una cartella e nelle sue sottocartelle. Usando la ricorsione non faremo altro che chiamare il metodo di conteggio dei file sulla cartella madre e su tutte le sottocartelle in essa contenute. Ecco il codice:

C_STRING(255;$1)
ARRAY TEXT($FolderList;0)
ARRAY TEXT($DocumentList;0)
C_LONGINT($piattaforma;$i)
C_LONGINT($0;$count)

  ` ottengo l'elenco di file e cartelle
FOLDER LIST($1;$FolderList)
DOCUMENT LIST($1;$DocumentList)
$count:=Size of array($DocumentList)

If (Size of array($FolderList)>0)
    For ($i;1;Size of array($FolderList))
        PLATFORM PROPERTIES($piattaforma)

    ` richiamo il metodo di conta su ogni
`sottocartella della cartella corrente
        If ($piattaforma#3)
           $count:=$count+ContaFile($1+$FolderList{$i}+":") `RICORSIONE
        Else
           $count:=$count+ContaFile($1+$FolderList{$i}+"\\")`RICORSIONE
        End if

    End for
End if

$0:=$count

Il metodo chiamante sarà:

$count:=ContaFile(Select folder("Scegli la cartella da contare"))
Info Info Gestire la memoria su Windows con 4D Customizer Plus
Tra le preferenze gestibili con 4D Customizer Plus c'è la quantità di memoria allocata su Windows, la voce "Main Memory Application". Per i valori da inserire si può fare riferimento alla faq sulla gestione della memoria.

Una cosa importante da tenere presente è che il valore che viene impostato nelle "Preferences" della struttura molto spesso non viene preso in considerazione. Infatti 4D controlla innanzitutto se la preferenza "Main Memory" è impostata su uno dei file delle preferenze; i file delle preferenze si trovano nella 4D folder (la cartella All users\Application Data\4D all'interno di Documents and setting o della cartella di sistema) e sono (nella 2003):

- 4D Preferences 2003.RSR per 4D, 4D Server, 4D Client, 4D Tools;
- 4D Runtime Preferences 2003.RSR per 4D Runtime, 4D Runtime Classic;
- 4D Engine Preferences 2003.RSR per l'Engine

Quindi se la preferenza della memoria è impostata sia nell'applicazione che nel file delle preferenze, quest'ultimo ha precedenza. Si possono allora avere questi casi:

- Se si configura la memoria attraverso le preferenze di 4d, queste impostazioni verranno inserite nel file delle preferenze (se contiene la risorsa "Main memory").
- Se si apre col Customizer il file delle preferenze, fare doppio clic sulla voce "Main Memory". Impostando qui un valore, questo verrà utilizzato al posto di quello settato nell'applicazione.
- Per utilizzare il settaggio della memoria eseguito sull'applicazione il file delle preferenze relativo deve essere cancellato dalla 4D folder.
- Il settaggio della memoria della struttura viene utilizzato solo quando questa viene poi inserita nell'Engine, avendo però al solito cura di aver cancellato il file "4D Engine Preferences 2003.RSR", che altrimenti ha priorità nella creazione dell'eseguibile.
Codice Codice Mostrare il Quick Report e il suo Wizard da progranmma
Il Quick Report e il suo Wizard sono disponibili da ambiente User, ma se volessimo permetterne l'uso anchedall'ambiente Custom in un proprio programma è possibile utilizzare il comando QR Report.
Il comando accetta fino a sei parametri - QR Report( {tabella;} documento {;hierarchical {;wizard {;search}}} {;*})- dove solo il primo è obbligatorio. Per visualizzare l'editor del Quick Report, basta passare il nome di un documento che non esiste (se esiste il QR Report lo apre e la calcola direttamente); impostando a True il parametro Wizard è possibile rendere disponibile all'utente anche questa funzionalità.

Di seguito riportiamo il codice per dare la possibilità all''utente di realizzare, stampare e salvare i propri report:

QUERY([Tabella])
If (OK=1)
  QR REPORT([Tabella];Char(1);False;True;False)
End if

Come puoi vedere, il parametro del documento è passato come il carattere Char(1), un nome di documento che non esiste, e il parametro wizard è passato come true.
Novizi Novizi Quando impostare i valori delle variabili
Quando si accede ad un INPUT FORM (o maschera di inserimento) è consigliabile impostare i valori delle variabili o dei campi inserendo questi assegnamenti nella zona "On Load" del metodo del form. Ad esempio:

Case of
    :(Form event=On Load)
        una_variabile:=3
End case

Nell'evento on Load vanno preimpostati anche i vari oggetti del form. Ad esempio, se ho un tab control che uso per scorrere le pagine del form, posso impostare "l'etichetta" che deve essere selezionata in partenza.

L'evento on Load può in realtà essere controllato anche all'interno di ogni singolo oggetto, ma in genere è meglio raccogliere tutte queste impostazioni in un unico punto, il metodo del form: in questo modo è possibile valutare tutte le preimpostazioni e decidere anche la precedenza o la priorità di alcune.
Ad esempio, se ho un menu a scelta che mi mostra/nasconde alcuni campi di inserimento, o se devo cambiare il colore di qualche oggetto (magari un campo è rosso se è obbligatorio quando un altro campo è preimpostato).
Plugin Plugin 4D View: selezionare una riga cliccando su una cella
Il comando PV SET AREA PROPERTY di 4D View permette di impostare una serie di proprietà dell'area. Ad esempio possiamo usarlo per fare in modo che, cliccando su una cella si selezioni automaticamente l'intera riga:

Case of
      :(Form event=On Load)
          PV SET AREA PROPERTY (area;pv select mode ;pv select single row )
          PV SET AREA PROPERTY (area;pv select highlight ;pv value on )
End case
Codice Codice Quale Runtime usare per distribuire un applicativo
Innanzitutto elenchiamo la corrispondenza dei nomi della versione 2004 in confronto con i precedenti:

4D Runtime Interpreted = 4D Runtime
4D Runtime Single User = 4D Runtime Classic
4D Runtime Volume License Light = 4D Engine Light Edition
4D Runtime Volume License Sponsored = 4DEngine Sponsored Edition Illimited
4D Runtime Volume License Pro = 4D Engine Pro Edition

Ecco riassunte le differenze:
4D Runtime Interpreted: gratuito, distribuzione illimitata, unico limite applicazioni non compilate, è separato dalla struttura (quindi non è una sola applicazione doppio cliccabile).
4D Runtime Single User: 106 Euro per un utente, per le applicazioni compilate, separato dalla struttura.
4D Runtime Volume License Light: gratuito con 4d o Server Developer, distribuzione illimitata, compilato come singola applicazione, unico limite la creazione di massimo 50 record per tabella (solo se si aggiungono, quindi posso usare una tabella piena con più di 50 record).
4D Runtime Volume License Sponsored : 1.069 Euro, distribuzione illimitata, compilato come singola applicazione, alla chiusura del programma appare per qualche secondo una finestra con scritto www.4d.com.
4D Runtime Volume License Pro : 2.674 Euro, fino a 1000 utenti, compilato come singola applicazione, senza finestra sponsor alla fine.

Tutti funzionano in sola lettura, ad esempio su CD.

Il modo più economico di sviluppare con 4d è comprare la versione di 4D Standard Edition da 299 Euro e distribuire il proprio applicativo con il 4D Runtime Interpreted, che è gratuito e senza altre limitazioni se non quello della compilazione.

Nota: I prezzi sono Iva esclusa e del listino della versione 2004; ad oggi (nov/04) è possibile comprare anche la versione 6.8 e 2003. Inoltre ogni tanto Italsoftware fa accordi con alcune riviste (Mac e/o Windows) per la distribuzione di una standard edition inclusa nel cd allegato, praticamente gratuita.
E' possibile, a meno delle nuove caratteristiche e di eventuali differenze estetiche, programmare con una versione precedente e distribuire il software con una versione più nuova del Runtime.
Codice Codice Controllare se Quicktime è installato su Windows
Mentre QuickTime è di default installato su Mac, la stessa cosa non si può dire su Windows. Per controllare se QuickTime è installato o meno è necessario utilizzare dei piccoli trucchi da codice. Ad esempio:

PICTURE TYPE LIST($array1;$array2)

Se la dimensione di uno di questi array è almeno uno, allora QuickTime è installato.
Un altro sistema potrebbe essere questo:

GET PICTURE RESOURCE(9000;$vPicture) `carica un'immagine
If (OK=1)
      PICTURE TO BLOB($vPicture;$myBlob;"JPEG")
End if

Il comando cerca di convertire l'immagine in JPEG. Se la dimensione del BLOB è zero, allora la conversione non è stata eseguita e quindi QuickTime non è installato.
Novita' Novita' 4D 2004: accesso alla cartella delle Licenze
Se l'attivazione del prodotto 4D è andata a buon fine, il file di licenza dell'applicazione è posizionato nella cartella Licenses.
Tutti gli altri prodotti 4D attivati e i prodotti aggiuntivi (Expansion) avranno i loro file di licenza nella stessa cartella.

Questa cartella Licenses può essere trovata in questi percorsi:
Windows: Documents and Settings\All Users\Application Data\4D\Licenses
Macintosh: Library:Application Support:4D:Licenses

Per trovarla direttamente da 4D basta andare nelle finestre di “Update License” e di “About 4D”, dove c'è un pulsante “Licenses Folder” che aprirà una nuova finestra della cartella Licenses.

(Fonte: Tech Tips #33938)
Codice Codice 4D e PHP
Per poter servire pagine dinamiche con Php generalmente si utilizza questa configurazione: web server + php +  backend verso un database Mysql o altro.
Php si integra facilmente con molti database SQL attraverso i vari moduli ODBC, ma non si integra direttamente con 4D Web Server (su Mac non c'è il driver ODBC per 4d) e non esiste un "4D open for php", quindi una soluzione può essere quella di usare i WebServices.

In una realtà in cui esiste già un server Apache + Php è stato sviluppato un database 4D.  
I dati di 4D accessibili via client vengono quindi anche mostrati in alcune pagine dinamiche offerte da Apache, non utilizzando 4D come Web Server, ma pubblicando alcune procedure come Web Services (SOAP).  

Ecco i passi indispensabili eseguiti in 4D:
1 - Il Server 4D installato ha le licenze web (nella versione 2004 è possibile acquistare solo il modulo per i webservices che costa meno) e il Web Server è attivo (Ambiente User: Menu Web Server : Start Web Server).
2 - Sempre lato server, impostare le Preferenze per i Web Services. In questo caso 4D fa da Server per le SOAP quindi verificare solamente le opzioni per Server Side. Sicuramente impostare "Allow Web Services Requests". Gli altri due parametri sono stati lasciati come di default.
3 - Creazione di un metodo usufruibile via Web Services: Menu Designer:New Method: wbSerTest. Per questo metodo dal menu Method:Method Properties: selezionare due opzioni da Attributes : Offered as a web Service, e Published in WSDL. Questo permette di richiamare il metodo come una funzione che dall'esterno accetta parametri e restituisce valori.
      
  ` metodo wbSerTest  offerto via SOAP  
C_INTEGER(in_RowQty; RowQty)
C_TEXT(AskDescr;in_AskDescr)
C_BLOB(Articles_Bl;Blob_tmp)
SET BLOB SIZE(Articles_Bl;0)
RowQty:=0
SOAP DECLARATION(AskDescr;Is Text ;SOAP Input ;"in_AskDescr")
SOAP DECLARATION(RowQty;Is Integer ;SOAP Input ;"in_RowQty")
SOAP DECLARATION(Articles_Bl;Is BLOB ;SOAP Output )
If (Length(AskDescr)>5)
    READ ONLY(*)
    If (RowQty<=0)
      RowQty:=10
    End if  
    QUERY([Articles];[Articles]Description= AskDescr)
    If (Records in selection([Articles])>0)
      ORDER BY([Articles];[Articles]Description;>)
      REDUCE SELECTION([Articles];RowQty)
      $tmp:=""
      While (Not(End selection([Articles])))
        $tmp:=[Articles]Description+Char(9)+[Articles]Code+Char(13)
        TEXT TO BLOB($tmp;Articles_Bl;3;*)
        NEXT RECORD([Articles])
      End while  
    Else  
      TEXT TO BLOB("empty query";Articles_Bl;3;*)
    End if  
End if  

  Lato Php:

require_once('nusoap.php');
//nusoap.php scaricabile a quest'indirizzo:

//http://dietrich.ganx4.com/nusoap/

//mi connetto al server sul quale "gira" 4d
// e' importante notare 4DSOAP come parte del percorso
$soapclient = new soapclient('http:///4DSOAP/');  
//Qui definisco i parametri da passare alla funzione
$parameters = array('in_AskDescr'=>valore1,'in_RowQty'=>valore2);
$ret =  $soapclient->call('wbserTest',$parameters);
if(!$ret)
  {
    print "Error:" . $soapclient->getError() . '\n<br><br>';
    exit;
  }
    else
    echo "Risultato : " . var_dump($ret) . '\n<br>';
  }
//Attenzione: se la funzione restituisce un parametro come risultato allora $ret sara' una
//variabile semplice altrimenti sara' un array associativo che avra' come chiavi i nomi delle
//variabili che restituisce la funzione .
//"Spengo il client"
unset($soapclient);
?>


Funziona.

Si ringrazia per il contributo Mauro Donadello
File File Component Query Editor
Ritenendo i component degli elementi importantissimi nella programmazione avevo promesso di fare un regalo a tutti gli sviluppatori 4D.

Il regalo era un component che noi usiamo pesantemente, Query Editor. Questo component permette ai vostri utenti di crearsi delle query e di averle a portata di click. Il query editor creato, si differenzia per la possibiltà di usare anche funzioni 4D. Intendo dire che si posso cercare elementi la cui data è la current date, oppure il campo di ricerca è il risultato di una request ecc... oltre alla possibilità di fare query by formula.

In allegato troverete il component e 2 manuali. Un manuale è quello di riferimento per il programmatore e l'altro è il manuale da dare all'utente finale.

Il component può essere usato solo con 4D 2003.5 e non con 4D inferiori o 4D 2004.

Così come è il component è in demo e lavora per un ora. Nel caso dopo le prove lo riteniate interessante e vogliate utilizzarlo nei vostri software mandatemi un mail di richiesta e vi verrà spedita la password.
Codice Codice Salvare il documento di un plugin
Usare SAVE RECORD per salvare un record che contiene un campo blob con una plugin area (tipo WriteArea_) non salva automaticamente il contenuto del campo come farebbe ACCEPT.

Questo perché per SAVE RECORD non vale il salvataggio automatico del blob-plugin nel campo che invece ACCEPT esegue. Per eseguire correttamente il salvataggio è quindi necessario fare manualmente la conversione a blob dell'area del plugin. Usando come plugin, ad esempio, 4D Write, la sequenza da seguire è:

[Tabella]WriteArea_:=WR AREA TO BLOB(WriteArea;1)
SAVE RECORD([Tabella])

Info Info Ordinare le colonne calcolate dei QR nella 2003
Una delle cosa di cui mi lamentavo del nuovo Quick Report della versione 2003 era la scomparsa della "Sorted" per le colonne: il che non era un problema per le colonne che contenessero dei campi del database (che potevano essere trascinate nella zona "Sort order"), ma lo diventava per i campi calcolati.

Ma mi sono ricavato uno stratagemma: prendo una colonna, la trascino nella zona "Sort order" e la colonna compare anche fra le colonne del report: un bel doppio clic sulla colonna e si apre l'Edit della stessa; inserisco il calcolo che voglio effettuare, premo OK e la colonna continua a risultare "ordinata". Posso quindi calcolare al solito break, subtotali, ecc...
Tecniche Tecniche I metodi nel Quick Report
Come citava una famosa rubrica, forse non tutti sanno che è possibile richiamare un metodo in una colonna di un quick report: basta semplicemente scriverne il nome e il valore ritornato dal metodo sarà il valore che prenderà la cella del report.

Ecco un esempio di uso: voglio avere un break in un report per Anno&Mese; se ordino per il campo Data il break sarebbe per giorno, quindi scrivo un metodo a cui passo il campo data.

`Da usare nel report, ritorna una stringa nel formato AAAAMM
$data_d:=$1
$0:=String(Year of($data_d))+String(Month of($data_d);"00")
Plugin Plugin Controllare lo stato di un host con NET_Ping
Usando gli Internet Command è possibile controllare se una macchina è in rete usando il comando NET_Ping. Ovviamente se l'host è protetto attraverso un firewall il comando potrebbe restituire risposta negativa.
Il comando NET_Ping riceve come parametri l'host, un testo da usare come dimensione del pacchetto da inviare, un longint che conterrà lo stato deoll'host e, opzionale, il tempo di timeout. Ecco un esempio di codice per controllare lo stato di un certo host:

`Method: Ping
C_TEXT($1)
C_INTEGER($alive)
$alive:=0
$errcode:=NET_Ping ($1;"test";$alive)
If ($errcode=0)
    If ($alive#1)
           ALERT($1+" non è attivo.")
    Else
           ALERT($1+" è attivo.")
    End if
Else
    ALERT("Errore # "+String($errcode))
End if
Tecniche Tecniche 4D 2004 Server Eseguibile e Cambio IP del server
Ecco una soluzione di un problema se producete un applicativo compilato con l'opzione server eseguibile con 4D 2004.

Una volta configurato il sistema, quindi server e client, provate e verificate le connessioni con il server, tutto funziona.
Decidete di cambiare il computer che svolge la funzione di server: se al nuovo server assegnate lo stesso indirizzo del vecchio server tutto rifunziona correttamente. Se usate una macchina con diverso indirizzo di IP, quando tenterete di riconnettervi dai client, dopo circa 30 secondi appare l'errore -10002 come se non trovasse il server.

Per rimediare su Windows bisogna eliminare il file: EnginedServer.xml
che si trova all'interno della cartella 4D Client dentro la cartella 4D Extension

Su MacOS X dovete eliminare lo stesso file.
Visualizzate il contenuto del pacchetto del client generato dal builder e troverete il file dentro la cartella 4D Extension.

Cosi facendo al successivo avvio vi verrà mostrata la finestra per scegliere nuovamente un'applicativo server.
Info Info Licenze 2004 : qualche informazione pratica *
Ecco alcune informazioni pratiche; occorre precisare che non sono ufficiali, per cui ci potrebbero essere errori o interpretazioni non corrette.

1. Per la registrazione del prodotto occorre un codice utente e la password registrati presso un sito di 4d ( in automatico porta al sito http://4dstore.4d.fr/ ). In teoria ogni utente dovrebbe registrarsi ma non credo ci siano problemi se si registra lo sviluppatore... almeno non tecnici. La registrazione per avere un proprio Account si può fare anche senza avere un prodotto da registrare.

2. Per ogni prodotto 4D esiste un Product Number unico.

3. Nella finestra di Unlock 4d devi scrivere: l'account, la relativa password e il Product Number (e un indirizzo email alternativo o ugale a quello dell'account)

4. Se la macchina non è collegata ad internet si può registrare sempre nella stessa schermata di Unlock4D un file da mandare sempre usando lo stesso sito http://4dstore.4d.fr/ ; il sito poi rimanda via mail un file di autorizzazione che letto sempre nella stessa finestra di 4d abilita il prodotto.

5. Il processo di abilitazione si può fare 3 volte; ovviamente dopo si può richiedere tramite il distributore Italsoftware lo sblocco.

6. Le licenze vengono registrate in un file nella cartella Licenses dentro la cartella 4D nel Sistema. L'informazione non è nascosta tanto che nella finestra di 4D Update License c'è proprio un pulsante che apre questa cartella. I file sono in formato HTML e presentano un elenco delle licenze attive.

7. Se ci si trova nei problemi l'Emergency mode attiva il prodotto solo inserendo il Product Number e senza bisogno di avere una connessione internet per 5 giorni, con l'unica limitazione di poterlo fare solo una volta al mese.

8. Invece la modalità Demo è sempre la stessa, cioè accesso in scrittura a max 20 metodi e 50 records.

Nota: link aggiornati, http://4dstore.4d.fr/ è più facile da usare.
Info Info Installare i numeri seriali di 4D
Per installare nuovi numeri seriali per 4D, in particolare quando si sta usando il Runtime, basta premere contemporaneamente i tasti Ctrl+Shift+F8 su Win e Command+Shift+F8 su Mac.

Per gli utilizzatori della 2004 è inoltre possibile usare il nuovo comando CHANGE LICENSES.
Tecniche Tecniche L'errore -10002 e le impostazioni TCP/IP
Il funzionamento del protocollo TCP/IP dentro 4D è assai semplice: quando 4th Dimension (o 4D Client o 4D Server) devono inviare un pacchetto in rete, il driver TCP/IP, attraverso comandi di basso livello, invia il pacchetto a destinazione. Se l'invio non va a buon fine 4D ritenta un certo numero di volte, fino a restituire il famigerato errore -10002.

In linea di massima se la rete è ben costruita, non ci sono particolari problemi di collisioni o di colli di bottiglia, è difficile vedere questo errore. Comunque, per chi avesse familiarità col registro di sistema di Windows, è possibile agire su alcune chiavi per fare in modo che 4D "ritenti più volte" prima di restituire errore (la cosa non risolverebbe ovviamente i problemi di rete, renderebbe solo il TCP/IP più tollerante).

I parametri si trovano nelle due sottochiavi:
HKEY_LOCAL_MACHINE\SYSTEM\CurrentControlSet\Services\Tcpip\Parameters
HKEY_LOCAL_MACHINE\SYSTEM\CurrentControlSet\Services\Tcpip\Parameters\Interfaces\ID della scheda
e sono i parametri KeepAliveInterval e TcpMaxDataRetransmissions.

Vista la pericolosità della modifica manuale di questi valori, è bene studiarne attentamente il comportamento, prima di apporre modifiche che potebbero rendere il sistema inutilizzabile.

Inoltre, almeno teoricamente, Windows XP ottimizza questi parametri in funzione dell'hardware usato.
Info Info Limitare l'accesso ad un plugin
E' possibile limitare l'accesso degli utenti ad un plugin utilizzando la finestra delle password di 4th Dimension: la funzione è utile soprattutto in installazioni client-server in cui il numero di licenze di un particolare plugin è inferiore al numero di client totali. Ad esempio, in un call center di 100 persone, potrei avere bisogno di solo 10 licenze write per i responsabili.

Entrando come Designer o Administrator nella dialog delle password creare un nuovo gruppo a cui assegnare gli utenti che avranno accesso al plugin. A questo punto scegliere dal menu Password la voce Plug-Ins Access per accedere alla finestra che permette di decidere quale gruppo (quello appena creato, ad esempio) può accedere a quale plugin.
Info Info Modificare tutte le password del sistema
Se l'ambiente password del database è rovinato o da ripristinare, è possibile crearne uno nuovo con 4D Insider, che permette di creare una nuova struttura senza password. Ecco le operazioni:
- aprire il database con l'Insider;
- creare un nuovo db;
- selezionare nel vecchio tutti gli elementi;
- trascinarli sul nuovo;
- salvare il nuovo database;
- impostare le nuove password con 4th Dimension.

Bisogna inoltre notare che:
- in relazione al numero di elementi del database, può essere necessario effettuare il trasferimento a piccoli pezzi;
- volendo esportare anche gli utenti, solo quelli creati dall'Administrator possono essere esportati.
Novizi Novizi Jumpstart 4D: ovvero un manuale completo di 4th Dimension
Uno degli aspetti più difficoltosi in cui un utente si imbatte iniziando a programmare con 4D è la vastità di informazioni dove attingere, dove per informazioni intendiamo ovviamente i manuali. Da quale parto? Cosa mi serve? E se non trovo ciò che mi serve?
Se le varie faq di introduzione a 4D sono troppo piccole, o se gli articoli di 4D sul passaggio da FileMaker Pro a 4th Dimension non vi hanno risolto il problema, un'altra strada da seguire potrebbe essere Jumpstart 4D, il manuale scritto da Steve Hussey che porta il programmatore ad avere una conoscenza di base ma completa del prodotto, partendo dalla struttura, passando poi agli ambienti User e Custom, password, compilazione e distribuzione del prodotto. Il tutto aiutato dalla costruzione guidata di un piccolo database di gestione fatture.

Le informazioni potete trovarle su 4D Press.
Jumpstart 4D è acquistabile per $29 in versione cartacea su Amazon o su 4D Store, oppure si può scaricare gratuitamente il PDF dal sito ftp di 4d.



1
Plugin Plugin 4D SDK: scrivere codice per 4th Dimension in C e C++
Un aspetto molto interessante di 4D è quello di permettere di scrivere delle routine in C o C++ da utilizzare all'interno dei programmi. Queste routine vengono generalmente chiamati plugin.
4D SDK è una raccolta di circa 500 comandi che permettono di integrare il codice scritto in C/C++ con 4th Dimension.

La maniera più rapida per scrivere un plugin è utilizzare 4D plug-in Wizard, un tool di 4D SDK nel quale è possibile definire nomi parametri e valori ritornati dei plugin. Vengono così generati una serie di file che costituiscono il punto di partenza del progetto.

A questo punto possiamo scrivere il nostro codice in C o C++ e quindi compilarlo.
Si piazza il plug-in nella cartella Win4dx o Mac4dx ed ecco che il comando creato in C è tranquillamente utilizzabile dentro 4th Dimension.
Web Web Comunicazione con Macromedia Flash
Ecco un esempio di Action Script per un pulsante in Flash che invia dei dati a 4D:

on (release) {
loadVariables ("http://www.sviluppo4d.it/4DCGI/web_esempio", _root, "POST");
}

"loadVariables" è una funzione Flash che chiama l'URL indicato. Modificando la funzione "loadVariables" nella palette Object Action, vedrai che ci sono delle scelte: "Send using POST" e "Send using GET". Così puoi inviare le variabili Flash a 4D, che da 4D leggi usando GET WEB FORM VARIABLES come se i dati venissero da una pagina HTML.

Per rimandare dei dati a Flash, puoi rispondere con una chiamata semplice del tipo:

SEND HTML TEXT("success=1&message="QUESTO VIENE DA 4D")

dove "success" e "message" saranno delle variabili in Flash. La chiamata "loadVariables" leggerà la risposta e invierà le informazioni alle variabili Flash.