28 de abril de 2008

Bar codes on Visual FoxPro reports

Traducción al inglés de Ricardo Aráoz, del artículo "Códigos de barras en informes de Visual FoxPro" publicado en español en PortalFox, para la comunidad de habla inglesa.


Bar codes on Visual FoxPro reports

NEW: Download freely the new FoxBarcode class to generate barcodes from Visual FoxPro, by clicking the following link: http://sites.google.com/site/foxbarcode

CONTENTS

This document describes the way to add bar codes to VFP reports using True Type fonts. The bar codes with which we'll deal here are :
  • Code 39
  • Code 128
  • EAN-13
  • EAN-8
  • Interleaved 2 of 5
In order to accomplish this we'll need, besides the True Type fonts, user defined VFP functions which will codify the input text into the proper format for each code and font. The True Type fonts and the functions can be downloaded from further down in this page.

At the bottom of this page you'll find examples on how to use these functions, a results sample from VFP reports, and notes for it's best usage. A VFP example with two reports may also be downloaded.

BAR CODES

We will briefly describe each possible code in order to help you choose the best fit for the app we will be deploying.

CODE 39

This is a variable length code, whose top length is given by the available printing space and the bar code reader we'll be using.

The character set of code 39 includes 43 characters : digits 0-9, letters A-Z (only upper case), space, and the following symbols: - + . / *. The "*" must be at the beginning and end of the code, so it shouldn't be used in an input text.

Each character is compose of 5 bars and 4 spaces, 3 of these 9 elements are wide (from there the name "3 of 9") and 6 thin elements.

Code 39 is composed of :
Start + InputText + End 

CODE 128

This is also a variable length code, and shorter than code 39.

Code 128 includes digits 0-9, letters A-Z (upper and lower case), and all standard ASCII characters (for a total of 128 characters, that's where the name comes from).

Code 128 is divided in three sub-sets A, B and C.
  • A includes: digits, upper case letters, and control codes.
  • B includes: digits, upper and lower case letters, and control codes.
  • C includes: only digits and compresses two digits into each character giving an excellent density.
This code has a control digit that offers more safety. This digit must be as follows:
* initial digit for subset A = 203
* initial digit for subset B = 204
* initial digit for subset C = 205
* final digit for all subsets = 206
sum = initial digit
for each inputCharacter
  sum = sum + (valueOfInputCharacter * position)
  CheckDigit = sum % 103
Code 128 is composed of :
Start + InputText + CheckDigit + End 

EAN-13 AND EAN-8 (European Article Numbering)

EAN-13 is used world wide for retail sales. It is of fixed length (13 characters). EAN-8 is a shortened version of EAN-13 which includes only 8 characers.

These codes are assigned and controlled by EAN International (http://www.ean.be) and authorized entities in every country.

In EAN-13, the first 6 digits stand for the country and company, the next 6 digits stand for the product, and the last digit is a control digit. Besides the country, the first two or three digits may stand for: books (ISBN), newspapers (ISSN), internal use, etc.

In EAN-8, the first 4 digits stand for the country and company, the next 3 digits stand for the product, and the last one is a control digit.

The control digit's algorithm is :
Sum = 0
for each InputDigit (12 or 7 digits)
  CorrectionValue = 1 if the digit's position is odd
                    3 if the digit's position is even
  Sum = Sum + ValueOfInputDigit * CorrectionValue
  CheckDigit = 10 - (Sum % 10)
  If CheckDigit = 10
    CheckDigit = 0
  endif 
EAN codes are composed of :
CountryCode + CompanyCode + ProductCode + CheckDigit

INTERLEAVED 2 OF 5

This is a variable length code, just like Code 39 and 128.

Interleaved 2 of 5 only includes digits 0-9 and fits two digits per character, giving a very good density. All Interleaved 2 of 5 codes have an even number of digits.

Every couple of digits is codified in a character composed by 5 bars and 5 spaces, 2 out of 5 bars are wide, hence the name "2 of 5".

This code has check digit by the following algorithm:
Sum = 0
for each InputDigit
  CorrectionValue = 1 if the digit's position is odd
                    3 if the digit's position is even
  Sum = Sum + ValueOfInputDigit * CorrectionValue
  CheckDigit = 10 - (Sum % 10)
  If CheckDigit = 10
    CheckDigit = 0
  endif 
Interleaved 2 of 5 is composed of:
Start + InputText + CheckDigit + End

TRUE TYPE FONTS

The true type fonts used in these examples are freeware files and work perfectly with the VFP functions included below.

The fonts used here may be downloaded from: fuentes.zip (30.9 Kb)

For better printing and readability of the codes our advise is to use the following font sizes:

True Type FontFileSize
PF Barcode 39PF_C39.ttf20 ó 22
PF Barcode 128PF_C128.ttf22, 24, 26 ó 28
PF EAN P36PF_EAN_P36.ttf36
PF EAN P72PF_EAN_P72.ttf72
PF Interleaved 2 of 5PF_I2OF5.ttf36 ó 48
PF Interleaved 2 of 5 WidePF_I2OF5_W.ttf28 ó 36
PF Interleavev 2 of 5 TextPF_I2OF5_Text.ttf28 ó 36

VFP FUNCTIONS

See below the VFP functions that transform an input text to the selected bar code.

These functions may be downloaded from: funcion.zip (2.2 Kb)
*------------------------------------------------------
* FUNCTION _StrTo39(tcString) * CODIGO 39
*------------------------------------------------------
* Converts a string to be printed with
* True Type Font "PF Barcode 39"
* USE: _StrTo39('Codigo 39')
* RETURNS: Character
*------------------------------------------------------
FUNCTION _StrTo39(tcString)
  LOCAL lcRet
  lcRet = '*' + tcString + '*'
  RETURN lcRet
ENDFUNC
*------------------------------------------------------
* FUNCTION _StrTo128A(tcString) * CODIGO 128A
*------------------------------------------------------
* Converts a string for printing with
* True Type Font "PF Barcode 128"
* Numerics and alphabetic (only upper case)
* If a character is not valid it is replaced by a space
* USE: _StrTo128A('CODIGO 128 A')
* RETURNS: Character
*------------------------------------------------------
FUNCTION _StrTo128A(tcString)
  LOCAL lcStart, lcStop, lcRet, lcCheck, ;
    lnLong, lnI, lnCheckSum, lnAsc
  lcStart = CHR(103 + 32)
  lcStop = CHR(106 + 32)
  lnCheckSum = ASC(lcStart) - 32
  lcRet = tcString
  lnLong = LEN(lcRet)
  FOR lnI = 1 TO lnLong
    lnAsc = ASC(SUBS(lcRet,lnI,1)) - 32
    IF NOT BETWEEN(lnAsc,0,64)
      lcRet = STUFF(lcRet,lnI,1,CHR(32))
      lnAsc = ASC(SUBS(lcRet,lnI,1)) - 32
    ENDIF
    lnCheckSum = lnCheckSum + (lnAsc * lnI)
  ENDFOR
  lcCheck = CHR(MOD(lnCheckSum,103) + 32)
  lcRet = lcStart + lcRet + lcCheck + lcStop
  *--- This changes spaces and invalid characters
  lcRet = STRTRAN(lcRet,CHR(32),CHR(232))
  lcRet = STRTRAN(lcRet,CHR(127),CHR(192))
  lcRet = STRTRAN(lcRet,CHR(128),CHR(193))
  RETURN lcRet
ENDFUNC
*------------------------------------------------------
* FUNCTION _StrTo128B(tcString) * CODIGO 128B
*------------------------------------------------------
* Converts a string to be printed with
* True Type Font "PF Barcode 128"
* Numerics and alphabetic (upper and lower case)
* If a character is not valid it is replaced by a space
* USE: _StrTo128B('Codigo 128 B')
* RETURNS: Character
*------------------------------------------------------
FUNCTION _StrTo128B(tcString)
  LOCAL lcStart, lcStop, lcRet, lcCheck, ;
    lnLong, lnI, lnCheckSum, lnAsc
  lcStart = CHR(104 + 32)
  lcStop = CHR(106 + 32)
  lnCheckSum = ASC(lcStart) - 32
  lcRet = tcString
  lnLong = LEN(lcRet)
  FOR lnI = 1 TO lnLong
    lnAsc = ASC(SUBS(lcRet,lnI,1)) - 32
    IF NOT BETWEEN(lnAsc,0,99)
      lcRet = STUFF(lcRet,lnI,1,CHR(32))
      lnAsc = ASC(SUBS(lcRet,lnI,1)) - 32
    ENDIF
    lnCheckSum = lnCheckSum + (lnAsc * lnI)
  ENDFOR
  lcCheck = CHR(MOD(lnCheckSum,103) + 32)
  lcRet = lcStart + lcRet + lcCheck + lcStop
  *--- This changes spaces and invalid characters
  lcRet = STRTRAN(lcRet,CHR(32),CHR(232))
  lcRet = STRTRAN(lcRet,CHR(127),CHR(192))
  lcRet = STRTRAN(lcRet,CHR(128),CHR(193))
  RETURN lcRet
ENDFUNC
*------------------------------------------------------
* FUNCTION _StrTo128C(tcString) * CODIGO 128C
*------------------------------------------------------
* Converts a string to be printed with
* True Type Font "PF Barcode 128"
* Only numeric characters
* USE: _StrTo128C('1234567890')
* RETURNS: Character
*------------------------------------------------------
FUNCTION _StrTo128C(tcString)
  LOCAL lcStart, lcStop, lcRet, lcCheck, lcCar, ;
    lnLong, lnI, lnCheckSum, lnAsc  
  lcStart = CHR(105 + 32)
  lcStop = CHR(106 + 32)
  lnCheckSum = ASC(lcStart) - 32
  lcRet = ALLTRIM(tcString)
  lnLong = LEN(lcRet)
  *--- La longitud debe ser par
  IF MOD(lnLong,2) # 0
    lcRet = '0' + lcRet
    lnLong = LEN(lcRet)
  ENDIF
  *--- Convert couple of digits to character
  lcCar = ''
  FOR lnI = 1 TO lnLong STEP 2
    lcCar = lcCar + CHR(VAL(SUBS(lcRet,lnI,2)) + 32)
  ENDFOR
  lcRet = lcCar
  lnLong = LEN(lcRet)
  FOR lnI = 1 TO lnLong
    lnAsc = ASC(SUBS(lcRet,lnI,1)) - 32
    lnCheckSum = lnCheckSum + (lnAsc * lnI)
  ENDFOR
  lcCheck = CHR(MOD(lnCheckSum,103) + 32)
  lcRet = lcStart + lcRet + lcCheck + lcStop
  *--- This changes spaces and invalid chara
  FOR lnI = 1 TO lnLong STEP 2
    lcCar = lcCar + CHR(VAL(SUBS(lcRet,lnI,2)) + 32)
  ENDFOR
  lcRet = lcCar
  lnLong = LEN(lcRet)
  FOR lnI = 1 TO lnLong
    lnAsc = ASC(SUBS(lcRet,lnI,1)) - 32
    lnCheckSum = lnCheckSum + (lnAsc * lnI)
  ENDFOR
  lcCheck = CHR(MOD(lnCheckSum,103) + 32)
  lcRet = lcStart + lcRet + lcCheck + lcStop
  *--- This changes spaces and invalid characters
  lcRet = STRTRAN(lcRet,CHR(32),CHR(232))
  lcRet = STRTRAN(lcRet,CHR(127),CHR(192))
  lcRet = STRTRAN(lcRet,CHR(128),CHR(193))
  RETURN lcRet
ENDFUNC
*------------------------------------------------------
* FUNCTION _StrToEan13(tcString,.T.) * CODIGO EAN-13
*------------------------------------------------------
* Converts a string to be printed with
* True Type Font "PF EAN P36" or "PF EAN P72"
* PARAMETERS:
*   tcString: 12 dígit string (0..9)
*   tlCheckD: .T. Only generates check digit
*             .F. Generates check digit and characters to be printed
* USE: _StrToEan13('123456789012')
* RETURNS: Character
*------------------------------------------------------
FUNCTION _StrToEan13(tcString,tlCheckD)
  LOCAL lcLat, lcMed, lcRet, lcJuego, ;
    lcIni, lcResto, lcCod, lnI, ;
    lnCheckSum, lnAux, laJuego(10), lnPri
  lcRet = ALLTRIM(tcString)
  IF LEN(lcRet) # 12
    *--- Error en parámetro
    *--- debe tener un largo = 12
    RETURN ''
  ENDIF
  *--- Generate Check digit
  lnCheckSum = 0
  FOR lnI = 1 TO 12
    IF MOD(lnI,2) = 0
      lnCheckSum = lnCheckSum + VAL(SUBS(lcRet,lnI,1)) * 3
    ELSE
      lnCheckSum = lnCheckSum + VAL(SUBS(lcRet,lnI,1)) * 1
    ENDIF
  ENDFOR
  lnAux = MOD(lnCheckSum,10)
  lcRet = lcRet + ALLTRIM(STR(IIF(lnAux = 0,0,10-lnAux)))
  IF tlCheckD
    *--- If I only generate a check digit
    RETURN lcRet
  ENDIF
  *--- To print with True Type Font PF_EAN_PXX
  *--- 1st. dígit (lnPri)
  lnPri = VAL(LEFT(lcRet,1))
  *--- Character code table
  *--- according to 'lnPri' (¡DO NOT TOUCH!)
  laJuego(1) = 'AAAAAACCCCCC' && 0
  laJuego(2) = 'AABABBCCCCCC' && 1
  laJuego(3) = 'AABBABCCCCCC' && 2
  laJuego(4) = 'AABBBACCCCCC' && 3
  laJuego(5) = 'ABAABBCCCCCC' && 4
  laJuego(6) = 'ABBAABCCCCCC' && 5
  laJuego(7) = 'ABBBAACCCCCC' && 6
  laJuego(8) = 'ABABABCCCCCC' && 7
  laJuego(9) = 'ABABBACCCCCC' && 8
  laJuego(10) = 'ABBABACCCCCC' && 9
  *--- Initial character (outside the code)
  lcIni = CHR(lnPri + 35)
  *--- Lateral and central characters
  lcLat = CHR(33)
  lcMed = CHR(45)
  *--- Rest of the characters
  lcResto = SUBS(lcRet,2,12)
  FOR lnI = 1 TO 12
    lcJuego = SUBS(laJuego(lnPri + 1),lnI,1)
    DO CASE
      CASE lcJuego = 'A'
        lcResto = STUFF(lcResto,lnI,1,CHR(VAL(SUBS(lcResto,lnI,1)) + 48))
      CASE lcJuego = 'B'
        lcResto = STUFF(lcResto,lnI,1,CHR(VAL(SUBS(lcResto,lnI,1)) + 65))
      CASE lcJuego = 'C'
        lcResto = STUFF(lcResto,lnI,1,CHR(VAL(SUBS(lcResto,lnI,1)) + 97))
    ENDCASE
  ENDFOR
  *--- Compose code
  lcCod = lcIni + lcLat + SUBS(lcResto,1,6) + lcMed + ;
    SUBS(lcResto,7,6) + lcLat
  RETURN lcCod
ENDFUNC
*------------------------------------------------------
* FUNCTION _StrToEan8(tcString,.T.) * CODIGO EAN-8
*------------------------------------------------------
* Converts a string to be printed with
* True Type Font "PF EAN P36" ó "PF EAN P72"
* PARAMETERS:
*   tcString: 7 digit string (0..9)
*   tlCheckD: .T. Only generates check digit
*             .F. Generates check digit and characters to be printed
* USE: _StrToEan8('1234567')
* RETURNS: Character
*------------------------------------------------------
FUNCTION _StrToEan8(tcString,tlCheckD)
  LOCAL lcLat, lcMed, lcRet, ;
    lcIni, lcCod, lnI, ;
    lnCheckSum, lnAux
  lcRet = ALLTRIM(tcString)
  IF LEN(lcRet) # 7
    *--- Parameter error
    *--- must have length = 7
    RETURN ''
  ENDIF
  *--- Generate check digit
  lnCheckSum = 0
  FOR lnI = 1 TO 7
    IF MOD(lnI,2) = 0
      lnCheckSum = lnCheckSum + VAL(SUBS(lcRet,lnI,1)) * 1
    ELSE
      lnCheckSum = lnCheckSum + VAL(SUBS(lcRet,lnI,1)) * 3
    ENDIF
  ENDFOR
  lnAux = MOD(lnCheckSum,10)
  lcRet = lcRet + ALLTRIM(STR(IIF(lnAux = 0,0,10-lnAux)))
  IF tlCheckD
    *--- If I only generate check digit
    RETURN lcRet
  ENDIF
  *--- To print with True Type Font PF_EAN_PXX
  *--- Lateral and central characters
  lcLat = CHR(33)
  lcMed = CHR(45)
  *--- Characters
  FOR lnI = 1 TO 8
    IF lnI <= 4
      lcRet = STUFF(lcRet,lnI,1,CHR(VAL(SUBS(lcRet,lnI,1)) + 48))
    ELSE
      lcRet = STUFF(lcRet,lnI,1,CHR(VAL(SUBS(lcRet,lnI,1)) + 97))
    ENDIF
  ENDFOR
  *--- Compose code
  lcCod = lcLat + SUBS(lcRet,1,4) + lcMed + SUBS(lcRet,5,4) + lcLat
  RETURN lcCod
ENDFUNC
*------------------------------------------------------
* FUNCTION _StrToI2of5(tcString) * INTERLEAVED 2 OF 5
*------------------------------------------------------
* Converts a string to be printed with
*   True Type Font "PF Interleaved 2 of 5"
*   or "PF Interleaved 2 of 5 Wide"
*   or "PF Interleaved 2 of 5 Text"
*   Only numeric characters
* USE: _StrToI2of5('1234567890')
* RETURNS: Character
*------------------------------------------------------
FUNCTION _StrToI2of5(tcString)
  LOCAL lcStart, lcStop, lcRet, lcCheck, ;
    lcCar, lnLong, lnI, lnSum, lnAux
  lcStart = CHR(40)
  lcStop = CHR(41)
  lcRet = ALLTRIM(tcString)
  *--- Generate check digit
  lnLong = LEN(lcRet)
  lnSum = 0
  lnCount = 1
  FOR lnI = lnLong TO 1 STEP -1
    lnSum = lnSum + VAL(SUBSTR(lcRet,lnI,1)) * ;
      IIF(MOD(lnCount,2) = 0,1,3)
    lnCount = lnCount + 1
  ENDFOR
  lnAux = MOD(lnSum,10)
  lcRet = lcRet + ALLTRIM(STR(IIF(lnAux = 0,0,10 - lnAux)))
  lnLong = LEN(lcRet)
  *--- Length must be even
  IF MOD(lnLong,2) # 0
    lcRet = '0' + lcRet
    lnLong = LEN(lcRet)
  ENDIF
  *--- Convert a couple of digits to character
  lcCar = ''
  FOR lnI = 1 TO lnLong STEP 2
    IF VAL(SUBS(lcRet,lnI,2)) < 50
      lcCar = lcCar + CHR(VAL(SUBS(lcRet,lnI,2)) + 48)
    ELSE
      lcCar = lcCar + CHR(VAL(SUBS(lcRet,lnI,2)) + 142)
    ENDIF
  ENDFOR
  *--- Compose code
  lcRet = lcStart + lcCar + lcStop
  RETURN lcRet
ENDFUNC
*------------------------------------------------------

USE EXAMPLES

These are some examples on how to use the conversion functions. All functions have the text to be codified as a CHARACTER parameter.

Example 1: To convert text "This is an example" to Code 128 B:
lcTexto = "This is an example"
lcCodBar = _StrTo128B(lcTexto)
? lcCodBar FONT "PF Barcode 128",36 
Example 2: To convert number 12345678 to Code 128 C (this code only takes even length numbers, if length is odd then it adds a "0" to the left of the number):
lcTexto = "12345678"
lcCodBar = _StrTo128C(lcTexto)
? lcCodBar FONT "PF Barcode 128",36
Example 3: To convert country Argentina ("779"), the company "1234" and the product "01234" to EAN-13 (I can only use 12 characters, the thirteenth character is the check code added by the function):
lcTexto = "779123401234"
lcCodBar = _StrToEAN13(lcTexto)
? lcCodBar FONT "PF EAN P72",72
Example 4: To code number 123456789 to Interleaved 2 of 5 (this code only takes even length numbers, if length is odd then it adds a "0" to the left of the number) with the three true type fonts:
lcTexto = "123456789"
lcCodBar = _StrToI2of5(lcTexto)
*-- I 2of5 Normal
? lcCodBar FONT "PF Interleaved 2 of 5",36
*-- I 2of5 Wide
? lcCodBar FONT "PF Interleaved 2 of 5 Wide",36
*-- I 2of5 Human Readable
? lcCodBar FONT "PF Interleavev 2 of 5 Text",36 
To use these codes in VFP reports, you must format the field with the chosen True Type font, and in the expression you must call the corresponding function passing as parameter the text to be codified.

To codify and show field MiCodigo from table MiTabla in a report field, in the field dialog window, we must input in expression:
_StrTo128B(MiTabla.MiCampo).
Another way is to create a cursor through a SELECT with a field with the data already formatted and generate the report from the cursor:
SELECT *, ;
  _StrTo39(MiTabla.MiCampo) as CodBar ;
  FROM MiTabla ;
  INTO CURSOR MiCursor 

RESULTS SAMPLE

These graphics where generated using the functions and fonts in this document, in VFP reports:

Código 39


Codigo 128 A


Código 128 B


Código 128 C


Código EAN 13


Código EAN 8


Código I 2de5 (ancho)


Código Interleaved 2de5


Código I 2de5 (lectura humana)


DOWNLOADS
FINAL NOTES

Once generated the bar code reports it is convenient to print them in laser printers due to their excellent definition, given this there'll be no problems with the reading of the bar codes.
With bubble jet printers, results are not optimum and maybe some bar code readers wont be able to read the code in the first scan.

These codes may also be placed in VFP forms, but only for visualizing, as no bar code reader will be able to read them from the monitor.

In order to use these functions in FoxPro for Windows, you will have to modify some commands that are not supported by FPW.

Up to the next time!

Luis María Guayán

25 de abril de 2008

FoxCharts (Traducción)

Artículo original: FoxCharts
http://weblogs.foxite.com/vfpimaging/archive/2008/04/04/5919.aspx
Autor: VFPIMAGING
Traducido por: Ana María Bisbé York


¿Desea crear algunos gráficos geniales en VFP?

¿Sin controles ActiveX, dlls o productos de terceros? ¿Qué piensa de estos gráficos?









Recientemente, tuve muchas discusiones con gente que pedía componentes para gráficos. Una vez que GdiPlusX nos brinda todas esas posibilidades, pensé que valdría la pena comenzar un proyecto sobre esto.

FoxCharts será una subclase de la clase ImageCanvas de GdiPlusX, que nos permite dibujar directamente sobre un objeto imagen, entre muchos otras características útiles y estupendas, que están fuera del alcance de este escrito.

Objetivos de FoxCharts:
  • Crear gráficos bonitos y modernos en puro VFP
  • No hay componentes ActiveX
  • Fácil de configurar
  • Fácil de personalizar.
  • Fácil de guardar en disco o imprimir
  • Código abierto (open source)
  • Aprovechar las capacidades de dibujos de GdiPlusX, que permiten a los usuarios modificar los gráficos de la forma que quieran.
  • Guardar como EMF, resultando gráficos perfectos al imprimir en informes VFP.

21 de abril de 2008

La función AERROR() retorna información ampliada para los errores 1104 y 1105

Artículo de la Base de Conocimientos de Microsft que indica que Visual FoxPro 9.0 SP2 mejoró la información de AERROR() para los errores 1104 y 1105.

El enlace al artículo original es el siguiente: AError() function returns extended information for errors 1104 and 1105 in Visual FoxPro 9.0 SP2

Estos errores son: Error al leer el archivo (1104) y Error al escribir el archivo (1105). El array producido por AERROR() contiene más información del error, que consiste en el número de error y mensaje de error del sistema operativo. El número de error y mensaje de error se almacenan en los elementos 6 y 7 del array. La información adicional de este error corresponde a los valores devueltos por la función GetLastError() de la API de Windows .

El siguiente ejemplo muestra una rutina ON ERROR que utiliza la función AERROR() para obtener mas información acerca del error 1104:

ON ERROR DO ErrHand      

Error 1104 && Esto causa el mensaje de error de lectura de archivo

ON ERROR

PROCEDURE ErrHand
   = AERROR(aErrorArray)
   DISPLAY MEMORY LIKE aErrorArray 
ENDPROC 

Si se tratara de un error 1104 real, el sexto elemento del array seria el número de error del sistema operativo, y el séptimo elemento es el mensaje de error del sistema operativo.

16 de abril de 2008

Búsquedas de archivos y texto con Filer.dll

El componente Filer.dll que se incluye desde las primeras versiones de Visual FoxPro nos brinda un motor de búsqueda de archivos y de texto.

Este componente DLL no tiene una interfaz de usuario. Se puede crear una instancia del objeto Filer en un programa, y buscar archivos y textos sin la intervención del usuario.

Una vez instanciado, se especifican las condiciones de búsqueda de archivos y/o texto, y se ejecuta el método Find(). Este método retorna un objeto colección con sus propiedades, que permiten obtener información de los archivos que cumplen las condiciones de la búsqueda; y métodos que permiten editar o eliminar estos archivos.

Ejemplos

1. Búsqueda del archivo "Customer.dbf":
LOCAL loFiler AS 'Filer.FileUtil'

*-- Creo el objeto
loFiler = CREATEOBJECT('Filer.FileUtil')
*-- Indico la ruta
loFiler.SearchPath = HOME(1)
*-- Indico el archivo o mascara (Ej: *.dbf)
loFiler.FileExpression = 'Customer.dbf'
*-- Indico que busque en subcarpetas
loFiler.SubFolder = 1
*-- Busco...
loFiler.Find(0)

IF loFiler.Files.Count > 0
  ? TRANSFORM(loFiler.Files.Count) + ' archivo/s encontrado/s'
  ?
  FOR lnCant = 1 TO loFiler.Files.Count
    WITH loFiler.Files.Item(lnCant)
      ? 'Archivo ' + TRANSFORM(lnCant)
      ? 'Ruta: ' + .Path
      ? 'Nombre: ' + .Name
      ? 'Tamaño: ' + TRANSFORM(.Size)
      ? 'Creado: ' + TRANSFORM(DATETIME(1899,12,30) + .DateTime * 86400)
      ? 'Modificado: ' + TRANSFORM(DATETIME(1899,12,30) + .LastWriteTime * 86400)
      ? 'Ultimo acceso: ' + TRANSFORM(DATETIME(1899,12,30) + .LastAccessTime * 86400)
      ?
    ENDWITH
  ENDFOR
ELSE
  ? 'El archivo no se encontró.'
ENDIF
loFiler = NULL
2. Búsqueda del texto "Microsoft" en archivos "*.TXT":
LOCAL loFiler AS 'Filer.FileUtil'

*-- Creo el objeto
loFiler = CREATEOBJECT('Filer.FileUtil')
*-- Indico la ruta
loFiler.SearchPath = HOME(1)
*-- Indico el archivo o mascara (Ej: *.dbf)
loFiler.FileExpression = '*.TXT'
*-- Indico que NO busque en subcarpetas
loFiler.SubFolder = 0
*-- Indico el texto a buscar
loFiler.SearchText1 = "Microsoft"
*-- Busco...
loFiler.Find(0)

IF loFiler.Files.Count > 0
  FOR lnCant = 1 TO loFiler.Files.Count
   *-- Edito el archivo
   loFiler.Files.Item(lnCant).Edit
  ENDFOR
ELSE
  MESSAGEBOX('No se encontró el texto en ningún archivo.',64,'Aviso')
ENDIF
loFiler = NULL

Notas adicionales

1. Visual FoxPro incluye un formulario de ejemplo, donde se muestra una interfaz de usuario para Filer.dll. Para ver este formulario, ejecute:
DO FORM (HOME(1) + 'Tools\Filer\Filer.scx')
Al ejecutar el formulario Filer.scx se agrega el elemento Filer al menú de Herramientas (Tools) de Visual FoxPro, que permanece por toda la sesión de VFP.
2. Las propiedades de fechas y horas (DateTime, LastWriteTime y LastAccessTime), retornan un valor numérico, donde la parte entera es la cantidad de días transcurridos desde el 30 de diciembre de 1899; y la parte fraccionaria es la fracción del día que determina la hora.

3. Todos los archivos que se encuentran en la carpeta \Tools\Filer del directorio de instalación de Visual FoxPro pueden ser distribuidos con nuestras aplicaciones.

4. Puede ver información mas detallada de todas las propiedades y métodos del objeto Filer, en la ayuda de VFP, bajo el tema Filer.dll.

Luis María Guayán

14 de abril de 2008

Objetos con fondos de colores degradados con GDI+ Revisado

Artículo original: GRADIENT OBJECTS WITH GDI+ REVISITED 
http://weblogs.foxite.com/vfpimaging/archive/2006/07/26/2076.aspx
Autor: Cesar Ch.
Traducido por: Ana María Bisbé York


He aquí la última versión de la clase GradientObjects.

Muchas gracias a: Craig Boyd, Bo Durban, Emerson S. Reed, Malcolm Greene, Keith Gordijn, Luis Navas, Ana Bisbe, Ailsom Heringerlos y todos los otros que han probado la clase, brindando valiosas opiniones y sugerencias.

La principal modificación de la versión anterior es la creación de una clase nueva, como sugirió Craig Boyd:

"... Crea una clase de usuario que ofrezca esta funcionalidad en lugar de codificar directamente en la clase botón.

Una clase que utilice bindevents para enlazar con los botones (o cualquier elemento de interfaz de usuario que se utilice) va a brindar un nivel adicional de abstracción. Esto permitiría a los desarrolladores utilizar la clase con cualquier otro elemento (por ejemplo, contenedores) y también brinda una vía rápida y fácil para añadir esta funcionalidad a aplicaciones existentes donde tienen sus propias subclases botones (o están utilizando las clases base de VFP).

Añade la clase al formulario y configura algunas propiedades."

Otra modificación importante fue "... cambiar cualquier control optionbutton o checkbox y establecer Graphical en su propiedad Style para el degradado del botón." como sugirió Bo Durban.

Emerson S Red brindó muchas imágenes para mostrar el comportamiento deseado para la clase.

Características:
  • Soltar una instancia de la clase en el formulario o contenedor, y todos los colores de los botones se convertirán en degradados. Se aplica a controles CommandButton, y CheckBox y ListBox gráficos.
  • Ofrece 9 tipos de degradado
  • Selecciona diferentes colores de fondo (BackColor) cuando el objeto tiene el foco y cuando no.
  • Crea imágenes con escalas de grises (GreyScale) para botones inhabilitados.
  • Define la posición de la imagen (Picture Position)
  • Convierte los bordes de la imagen en trasparente (independientemente del color que utilice)
  • Las teclas de acceso directo (\<) especificadas en la propiedad Caption se muestran normalmente y mantienen su funcionamiento.




Cómo se crea:
  • La clase crea un archivo de imagen nuevo que imita la apariencia original del botón, utilizando todas las propiedades de texto, como Caption, Fontbold, FontItalic y la imagen original, todo redibujado con un fondo con gradiente especificado.
  • El uso extensivo de GDI+ para crear imágenes, 100 % con llamadas directas a API, para obtener la máxima velocidad en la creación de imágenes que serán sustituidas en los botones.
  • En el primer momento, se crean solamente las imágenes principales de los botones. Solamente cuando el ratón o el foco está sobre un objeto específico que crea esa clase en la "Imagen seleccionada". Lo mismo ocurre con "Escalas de grises de imágenes inhabilitadas".
  • Muchos eventos se controlan utilizando BindEvents, como: MouseMove, MouseEnter, GotFocus, LostFocus.
  • Se crearon tres tipos de gradientes diferentes utilizando funciones diferentes.
  • Para los que tengan GradientModes de 1 a 4, utilizados GdipCreateLineBrushFromRectI para crear un gradiente sencillo con brocha lineal con 2 colores.
  • Para los que tengan GradientModes de 5 a 8, GdipCreateLineBrushFromRectI se emplea nuevamente en combinación con GdipSetLinePresetBlend, que de acuerdo con MSDN "define matrices de colores y posiciones utilizadas para interpolar mezclas de colores en un degradado multicolor." Fue utilizado para crear un color 3 de degradado comenzando por la propiedad BackColor1 con BackColor2 en el centro y nuevamente BackColor1 en la otra punta.
  • Para los que tengan GradientModes igual a 9, he creado un PathGradientBrush utilizando GdipCreatePath, GdipAddPathRectangle, GdipCreatePathGradientfromPath, GdipSetPathGradientCenterColor y GdipSetPathGradientSurroundColor. Hay un ejemplo muy bueno utilizado en el último artículo de Craig Boyd publicado en FoxTalk 2.0. El color que rodea está definido por la propiedad BackColor1. El color del centro está definido por BackColor2.
  • La clase GDI+ ImageAttributes es utilizada para aplicar una matriz de color para crear imágenes GreyScale (con escalas de grises) para botones inhabilitados. Se utiliza también para crear un fondo transparente de las imágenes utilizando la función RemapTable.
  • Las teclas de acceso directo están habilitadas utilizando el StringFormatHotKeyPrefix
Propiedades a configurar en la clase

Vea la ficha Favoritos en la ventana Propiedades.


  • BackColor1 - Numérico, el valor RGB del color inicial del fondo degradado cuando el objeto NO tiene el foco y el ratón no se está moviendo sobre el.
  • BackColor2 - Numérico, el valor RGB del color de destino del fondo degradado cuando el objeto NO tiene el foco y el ratón no se está moviendo sobre el.
  • SelBackColor1 - Numérico, el valor RGB del color inicial del fondo degradado cuando el objeto tiene el foco o el ratón se está moviendo sobre el.
  • SelBackColor2 - Numérico, el valor RGB del color de destino del fondo degradado cuando el objeto tiene el foco o el ratón se está moviendo sobre el.
  • CaptionForeColor - Numérico, si se configura, sobreescribe el color original establecido para los controles por la propiedad ForeColor.
  • CaptionBold - Lógico, si se configura, sobreescribe el aspecto original establecido para los controles por la propiedad CaptionBold.
  • CreateBorder - Lógico, establece el borde con el color de BackGround1.
CreateBorder = .T.

CreateBorder = .F.
  • GradientMode - Numérico, de 1 a 9, determina el tipo de degradado a aplicar en los botones.
1 - Horizontal 2 - Vertical 3 - Diagonal1 4 - Diagonal2 utilizando una brocha lineal con dos colores
5 - Horizontal 6 - Vertical 7 - Diagonal1  8 - Diagonal2 utilizando una brocha lineal con tres colores, donde el color que rodea es BackColor1 y el color de centro es BackColor2
9 Tipo PathGradient rectangular, donde el color que rodea es BackColor1 y el color de centro es BackColor2


  • ReduceColorLevel - Numérico, configura automáticamente el color de destino del degradado (backcolor2 y selbackcolor2) desde 0 (no cambia) a 100 (blanco). Si es .F., no se aplica el cambio y se utilizará el valor original de backcolor2 y selbackcolor2.
  • CreateDisabledPicture - Lógico, .T. = crear una imagen con escala de grises de los botones para ser usadas cuando se desactivan uno o más controles.
  • MouseDownEffect - Lógico, determina si el botón se oscurecerá o se aclarará cuando baja el ratón
  • MouseDownBrightness - Numérico, porcentaje de luminosidad, -100 = Negro,0 = no cambia; +100 = Blanco
  • TranspImgBack - Lógico, determina si la pintura tendrá su fondo convertido a transparente (alpha = 0). De forma predeterminada, la clase admitirá el color del primer pixel, coordenadas (0,0) tiene fondo transparente, y sustituye todos los colores que coincidan con este color por transparente.
Redibujar un objeto específico

En muchos casos, necesitamos cambiar por código algunas propiedades de algunos botones, como su título (Caption). Cuando utilice esta clase, usted necesita llamar al método UpdateControl de la clase, como en el ejemplo siguiente:
Thisform.CmdButton1.Caption = "New Caption"
Thisform.CmdButton1.FontItalic = .T.
Thisform.Gradobjects1.UpdateControl(Thisform.CmdButton1) 

Asignar diferentes efectos a algunos botones específicos

Es muy fácil de conseguir. Ponga el botón de comando en un contenedor aparte y suelte una instancia de la clase, configurando sus propiedades según sea necesario.

Debajo se muestra un formulario en modo desarrollo y el resultado, cuando se ejecuta.





Esta es la primera versión de esta clase, seguramente necesitará más pruebas. Si encuentra algún error, dígamelo, por favor! Todas las sugerencias serán muy bien recibidas.

Puede descargar la clase de este enlace: http://www.geocities.com/macmarbr/gradobjects.zip

Algunos enlaces relacionados:

Alguna información extra sobre este tema podrá encontrar en:
31-07-06

Reparado: Ahora trabajan normalmente las teclas de acceso directo en títulos (Caption) "\<"

Reparado: Cuando pasa el ratón sobre un botón que tiene el foco se mantiene el efecto "seleccionado".

Reparado: Si un usuario elimina el valor de la propiedad Backcolor y lo cambia a (Ninguno) el control fuerza al predeterminado. Es una medida de seguridad, gracias a Bernard Bout.

Actualización: Nuevo formulario que muestra cómo agregar gradientes diferentes a un formulario. Ver "twoGradients.scx" en la carpeta principal.

03-08-06

Reparado: Error en LOSTFOCUS / GOTFOCUS

Actualización: El método "UpdateControl" permite cambiar el color de los objetos

Actualización: El método "ChangeColors" admite valores desde -100 a 100. -100 = Negro; 0 = No cambia;  +100 = Blanco

Actualización: Formulario "TestGradAnimation.scx" que muestra como hacer un botón "Parpadeo" con los efectos

Actualización: Nuevas propiedades "MouseDownEffect" y "MouseDownBrightness"

MouseDownEffect - Lógica, determina si el botón se oscurecerá o aclarará cuando el ratón está presionado

MouseDownBrightness - Numérico, Luminosidad -100 = Negro; 0 = No cambia;  +100 = Blanco

Nota

Esta clase es totalmente gratis. La información brindada en esta página y el código descrito en este artículo no tiene garantía absoluta. úselo bajo su propio riesgo.

7 de abril de 2008

Programa para comprimir en zip utilizando el Shell de Windows

Después de estar buscando varias opciones para comprimir archivos en zip, encontré la que, a mi punto de vista es mas práctica y no necesita dll adicionales, anexo os pongo el prg a ver si os sirve.

Nota del Editor: Código similar al ejemplo enviado por Moby (Guatemala) y publicado en: Crear un Zip usando los recursos estandard de Windows
***************************************************************
* COMPRIMIRENZIP.PRG 
* Se encarga de comprimir en un archivo zip varias extensiones de Archivos 
* utilizando el Shell de Windows - probado en XP 
* FECHA : 19 MARZO 2008 
* HECHO POR : ROTEROL (Rubén V. Otero L.) 
* La Coruña, España
****************************************************************

LOCAL lcExtension, lcZip && Variables locales tipo Caracter
LOCAL lnArchivos, lnContArray, lnContArrayDef && Variables locales tipo Numérico
LOCAL loZip, loShell, loFolder && Variables locales tipo Objeto
LOCAL ARRAY laArchivos[1,1], laArcDef[1], laExtensiones[6] && Variables locales tipo Array

*!* Verifico todos los ficheros a comprimir y los guardo en un array único laArcDef
*!* No puedo hacer una sola instrucción adir con todas las extensiones de archivos, con lo cual,
*!* ejecuto tantos adir como sea necesario para almacenar los 
*!* *.dbc, *.dct, *.dcx, *.dbf, *.fpt y *.cdx
laExtensiones[1] = "*.dbc"
laExtensiones[2] = "*.dcx"
laExtensiones[3] = "*.dct"
laExtensiones[4] = "*.dbf"
laExtensiones[5] = "*.fpt"
laExtensiones[6] = "*.cdx"
FOR EACH lcExtension IN laExtensiones
  lnArchivos = ADIR(laArchivos,lcExtension)
  *!* Dependiendo si es la primera vez que paso por el For Each, declaro el array laArcDef con el
  *!* número de Archivos resultantes del Adir, Si no es la primera vez que paso por el For Each,
  *!* incremento el número de elementos de laArcDef en la longitud que tiene actualmente 
  *!* mas el número de archivos resultantes del adir
  IF lcExtension=laExtensiones[1]
    lnContArrayDef = 0
    DECLARE laArcDef[lnArchivos]
  ELSE
    lnContArrayDef = ALEN(laArcDef)
    DECLARE laArcDef[ALEN(laArcDef)+lnArchivos]
  ENDIF
  FOR lnContArray = 1 TO lnArchivos
    lnContArrayDef = lnContArrayDef + 1
    laArcDef[lnContArrayDef] = ADDBS(FULLPATH(CURDIR()))+laArchivos[lnContArray,1]
  NEXT
NEXT

lcZip = ADDBS(FULLPATH(CURDIR()))+'ArchivoComprimido.zip'
IF FILE(lcZip) && Borro Zip si existe
  ERASE lcZip
ENDIF

*!* Creo Fichero Encabezado de zip
STRTOFILE(CHR(0x50)+CHR(0x4B)+CHR(0x05)+CHR(0x06)+REPLICATE(CHR(0),18),lcZip)

oShell = CREATEOBJECT("Shell.Application")

IF TYPE('oShell')='O'
  *!* Según Investigué, Microsoft recomienda crear el Objeto oFolder y trabajar con ese objeto 
  *!* para hacer la instrucción copyHere intenté hacerlo directamente
  *!* -oShell.NameSpace("&lcZip").copyHere(laArcDef[lnContArray])-, pero recibía contínuos errores de
  *!* fallo de aplicación VFP. asimismo, tuve que crear el objeto oFolder con la macrosubstitución
  *!* -oShell.NameSpace("&lcZip")- por que tambien, depurando el programa, detecté que no se 
  *!* creaba el objeto oFolder colocando la instrucción -oShell.NameSpace("&lcZip")- directamente
  oFolder = oShell.NameSpace("&lcZip")
  IF TYPE('oFolder')='O'
    FOR lnContArray = 1 TO lnContArrayDef && ALEN(laArcDef)
      WAIT 'Procesando Archivo '+LOWER(laArcDef[lnContArray])+', '+;
        ALLTRIM(STR(lnContArray*100/lncontArrayDef))+'%' WINDOW NOWAIT
      oFolder.CopyHere(laArcDef[lnContArray])
      *!* Me veo obligado tambien a colocarle un inkey por que si no se pone y por ejemplo
      *!* tenemos 48 archivos para comprimir (como es mi caso), el proceso lo efectúa muy rapido,
      *!* y aún cuando sale del for...next, se crean tantos shell de Fox
      *!* como archivos haya, con el dialogbox de "Comprimiendo..."
      INKEY(0.5)
    NEXT
    WAIT CLEAR
    oFolder = .F.
  ELSE
    MESSAGEBOX('No pudo crearse el Objeto oFolder',16)
  ENDIF
  oShell = .F.
ELSE
  MESSAGEBOX('No pudo crearse el Objeto Shell',16)
ENDIF
Rubén V. Otero L.