31 de marzo de 2018

EditSource()

La nueva función EditSource() en VFP7 y superior es particularmente práctica en por lo menos 3 escenarios.

1) Comunicandose con otro desarrollador con respecto a un código en una localización particular de un método o PRG. Por ejemplo, al reportar un "bug" al desarrollador, en lugar de escribir algo parecido a esto al describir donde está el problema:

MODI CLASS AlgunaClase OF AlgunVCX METHOD AlgunMetodo
*-- ir a la línea 17
*-- or 
*-- ir a la línea IF VARTYPE(AlgunaVariable) = 'U'

Ud. solo puede escribir:

EditSource("AlgunVCX.VCX",17,"AlgunaClase","AlgunMetodo")

y a continuación describir el problema.

2) Escribir a un desarrollador que abra el programa, clase, o formulario en una línea específica del código.

3) Insertando referencias de EditSource() en comentarios de código, así que cualquier lectura del desarrollador, puede simplemente seleccionar el texto del EditSource() y hacer Click Derecho y ejecutar la selección desde el menú contextual.

Clases:

EditSource("MyLib.VCX",0,"ClassName")

... equivalente a MODI CLASS ClassName OF MyLib

Métodos de clases:

EditSource("MyLib.VCX",2,"ClassName","Init")

... equivalente a MODI CLASS ClassName OF MyLib METHOD INIT, pero con la ayuda agregada de especificar el número de la línea donde se coloca el cursor.

Métodos de clases miembros:

EditSource("MyLib.VCX",2,"ClassName","Member.Init")

... equivalente a MODI CLASS ClassName OF MyLib METHOD Member.Init, pero con la ayuda agregada de especificar el número de la línea donde se coloca el cursor.

Formularios

EditSource("MyForm.SCX")

... equivalente a MODI FORM MyForm

Métodos de formularios:

EditSource("MyForm.SCX",6,"","Form1.Load")

... equivalente a MODI FORM MyForm METHOD LOAD, pero con la ayuda agregada de especificar el número de la línea donde se coloca el cursor.

Metodos miembros de formularios:

EditSource("MyForm.SCX",13,"","Form1.Member.Init")

... equivalente a MODI FORM MyForm METHOD Member.Init, pero con la ayuda agregada de especificar el número de la línea donde se coloca el cursor.

Programas:

EditSource("MyPRG.PRG")

... equivalente a MODI COMM MyPRG

Programas:

EditSource("MyPRG.PRG",10)

... equivalente a MODI COMM MyPRG, pero con la ayuda agregada de especificar el número de la línea donde se coloca el cursor.

Como el número exacto de la línea puede cambiar, al llamar EditSource() desde el código de un desarrollador que cambio el código fuente por modificaciones o la inserción de comentarios, usted puede determinar programaticamente el número de la línea deseada antes de llamar EditSource(). Ud. puede usar un PRG como el ejemplo:

local laLines[1], lnLine, xx
alines(laLines,filetostr("Test.PRG"))
FOR xx = 1 to alen(laLines,1)
  IF laLines[xx] = 
    lnLine = xx
    exit
  ENDIF
ENDFOR
EditSource("Test.PRG",lnLine)

EditSource() dispara un NOWAIT implícito de modo que usted pueda tomar la acción de seguimiento en el código del desarrollador que ejecuta EditSource(). Por ejemplo, para destacar una línea específica del código, ejecute:

keyboard "{Shift+End}" 

ó

keyboard "{Shift+DnArrow}"

inmediatamente después del comando EditSource().

Copie y pegue el código siguiente en la ventana de comandos, y después lo ejecuta para ver el efecto:

text to lcString noshow
* test.prg
aa = 100
bb = 200
cc = 300
return aa + bb + cc
endtext
strtofile(lcString,"test.prg")
text to lcString noshow
* test1.prg
editsource("test.prg",3)
keyboard "{Shift+DnArrow}"
endtext
strtofile(lcString,"test1.prg")
do test1

[025] VFP Tips & Tricks - Drew Speedie

27 de marzo de 2018

Función ATAGINFO()

VFP 7.0 agregó una la nueva función ATAGINFO() para llenar un Array con la información sobre las etiquetas contenidas en un archivo de indice CDX.

use <AlgunaTabla> in 0<
ataginfo(laTemp,"","AlgunaTabla")
display memory like laTemp

[035] VFP Tips & Tricks - Drew Speedie

23 de marzo de 2018

Obtener todos los objetos de un formulario

Una Clase para obtener todos los objetos de un formulario y retornarlos dentro de un objeto Collection (VFP8 en adelante) mejorando por tanto la manera de acceder a ellos por clave.

******** CODIGO DE EJEMPLO *******
_SCREEN.ADDOBJECT('txtFldA', 'TextBox')
_SCREEN.ADDOBJECT('txtFldB', 'TextBox')
_SCREEN.ADDOBJECT('txtFldC', 'TextBox')
_SCREEN.ADDOBJECT('txtFldD', 'TextBox')
LOCAL loCol AS Collection
loCol = CREATEOBJECT('collection')
? COBJECTS(loCol, _SCREEN)
? loCol(3).NAME
? loCol(3).VISIBLE
loCol('txtFldC').TOP   = 100
loCol('txtFldC').VALUE = 'Prueba'
loCol('txtFldC').VISIBLE = .T.
*!* Nota: Es conveniente que la vida de la coleccion sea la mas corta posible
*!*       con el fin de evitar errores de garbaje o de collecion de basura ya que
*!*       la coleccion guarda refrencias a muchos objetos cuya vida no se asegura
RELEASE loCol
RETURN
**********************************
*!* Devuelve todos los objetos contenidos en contenedor
*!* Sintaxis:   COBJECTS(toCol, toCnt)
*!* Retorno:    lnRetVal
*!* Argumentos: toCol especifica la coleccion de retorno con los objetos contenidos
*!*             debe declararse en el programa que invoca la funcion y pasarse 
*!*             por referencia toCnt especifica el objeto contenedor que se va a examinar
*!*
*!* Nota:       Como la funcion es recursiva para darle mas rapidez no hacemos 
*!*             comprobaciones sobre la validez de los parametros enviados
*!*             por lo que confiamos que toCol se haya declarado, pasado por 
*!*             referencia y que toCnt realmente sea un objeto contenedor
FUNCTION COBJECTS
  LPARAMETERS toCol AS Collection, toCnt AS OBJECT
  LOCAL loCtrl AS OBJECT, loPage AS OBJECT, loColu AS OBJECT, lcClass AS CHARACTER, ;
  lnKey AS INTEGER, lcKey AS CHARACTER, lnRetVal AS INTEGER

  FOR EACH loCtrl IN toCnt.CONTROLS
    *!* Valores
    lcClass = UPPER(loCtrl.BASECLASS)
    *!* Buscar una key valida
    FOR lnKey = 0 TO 32767
      lcKey = loCtrl.NAME + ALLTRIM(TRANSFORM(lnKey, @Z'))
      IF toCol.GetKey(lcKey) = 0
        EXIT
      ENDIF
    ENDFOR
    *!* Agregar control a la coleccion
    toCol.ADD(loCtrl, lcKey)
    *!* Recursivo
    DO CASE
      CASE  lcClass = 'PAGEFRAME'
        *!* Recorrer las paginas
        FOR EACH loPage IN loCtrl.PAGES
          COBJECTS(toCol, loPage)
        ENDFOR
      CASE lcClass = 'GRID'
        *!* Recorrer las columnas
        FOR EACH loColu IN loCtrl.COLUMNS
          COBJECTS(toCol, loColu)
        ENDFOR
      CASE lcClass = 'CONTROL'
        *!* El un objeto control no puede ser recorrido
        *!* aunque tenga la propiedad ControlCount
      CASE PEMSTATUS(loCtrl, 'ControlCount', 5)
        *!* Recorre objeto contenedor
        COBJECTS(toCol, loCtrl)
      CASE PEMSTATUS(loCtrl, 'ButtonCount', 5)
        *!* Recorre botones
        FOR EACH loCtrl IN loCtrl.BUTTONS
          *!* Buscar una key valida
          FOR lnKey = 0 TO 32767
            lcKey = loCtrl.NAME + ALLTRIM(TRANSFORM(lnKey, @Z'))
            IF toCol.GetKey(lcKey) = 0
              EXIT
            ENDIF
          ENDFOR
          *!* Agregar control a la coleccion
          toCol.ADD(loCtrl, lcKey)
        ENDFOR
    ENDCASE
  ENDFOR
  *!* Valor retorno
  lnRetVal = toCol.COUNT
  *!* Retorno
  RETURN lnRetVal
ENDFUNC
**********************************

Enjoy It...

Alexandre Hedreville

17 de marzo de 2018

Ordenar

Artículo original: Sorting
http://www.foxpert.com/KnowlBits_200609_4.htm
Autor: Christof Wollenhaupt
Traducido por: Ana María Bisbé York


En la mayoría de los lenguajes de programación se encuentran algoritmos de ordenamiento; pero es difícil encontrar la implementación de algoritmos de ordenamiento en Visual FoxPro. Por supuesto, Calvin Hsia tiene algunos en su Blog; pero es muy difícil encontrar otros. La razón más obvia es que Visual FoxPro admite ordenar de forma nativa tanto por índices como por matrices.

Ordenar cadenas mixtas

Si tiene los datos del tipo

1/1A
1/1B
2/A
10/2
10/3

Puede querer ordenar la primera parte numéricamente. Es decir, el orden debe ser 1, 2, 10 y no 1, 10, 2 como ocurriría si Visual FoxPro lo trata como cadena. Al mismo tiempo, sin embargo, desea ordenar la parte restante en orden alfabético. La siguiente expresión de índice lo soluciona:

INDEX ON STR(VAL(field)) + field TAG _Sort

Ordenar con COLLATE

Virtualmente cada aplicación tiene solamente una secuencia COLLATION que utiliza cada vez. La mayoría utiliza MACHINE, seguido por GENERAL, seguido por versiones específicas del país. Sin embargo, a veces necesita ordenar y buscar los datos con orden diferente. Por ejemplo, puede utilizar MACHINE como secuencia collate; pero en algunos casos para ordenar casos independientes (¡esto es sólo un ejemplo!)

Cuando usted crea un índice, Visual FoxPro utiliza la secuencia COLLATE activa o alguna que esté especificada en la cláusula COLLATE. Un detalle conocido es que Visual FoxPro siempre utiliza la secuencia COLLATE que pertenece a un índice cuando Visual FoxPro busca u ordena utilizando los índices. Esto es verdad incluso cuando la aplicación activa una secuencia COLLATE diferente.

Create Cursor curTest (cName C(10))
Insert into curTest Values("Straße")
Set Collate To "GERMAN"
Index on cName Tag cName
Set Collate To "MACHINE"
? Seek("Strasse") && Muestra .T.

13 de marzo de 2018

Programa usando la Función ShellExecute

Codigo usando la Función ShellExecute para llamar al programa de correo predeterminado, el cual en mi caso es Mozilla-Mail.

Primero Declaro la Función en un Prg que he definido como Principal:

DECLARE INTEGER ShellExecute IN shell32.dll ;
  INTEGER hndWin, STRING cAction, STRING cFileName, ;
  STRING cParams, STRING cDir, INTEGER nShowWin

Luego en un botón de comando en un form, llamo a la función asi:

LOCAL dir1,correo
dir1 = ALLTRIM(Thisform.txtEmail.Value)
correo = "mailto:" + dir1
if not empty(dir1)
  ShellExecute(0,"open",correo,"","",1)
  ***//mozilla.exe -compose "to=foo@nowhere.net"//
else
  MESSAGEBOX("No hay Dirección de Correo",0,"E-mail")
endif

Espero que les sea de utilidad

Hasta pronto.

Josue Nahum Montufar

8 de marzo de 2018

Enter vertical en un Grid

Esta es una solución planteada por mi compañero de trabajo Pedro Valle para lograr un Enter vertical en un Grid de modo mas eficiente.

He visto soluciones anteriores pero el foco lo obtenía siempre la siguiente columna después de haberse movido el puntero a la siguiente fila y el código estaba dado en el evento Keypress. Necesitábamos que después del Enter el foco se mantuviera en la misma columna.

En esta nueva solución el código lo insertamos en el evento GotFocus de la columna siguiente a la que se le hizo el Enter. En el caso de que la columna en la que se hizo el Enter sea la ultima del Grid, la columna siguiente será la primera.

Llamaremos ColumnX a la columna donde se hará el Enter.

IF LASTKEY() = 13
  CLEAR TYPEAHEAD
  KEYBOARD '#' CLEAR
  SKIP
  Thisform.Grid1.Refresh()
  Thisform.Grid1.ColumnX.SetFocus()
ENDIF

Saludos.

Alex Moreno Candiotty

1 de marzo de 2018

Consultas de mas de 256 caracteres al servidor remoto (SPT y TEXT.. ENDTEXT)

Una pregunta común, con una respuesta sencilla: el uso de bloques TEXT .. ENDTEXT da enorme flexibilidad a las aplicaciones Cliente-Servidor.

Como es bien sabido, las constantes de tipo caracter de VFP están limitadas a 255 caracteres, por lo que pudiera dejarnos limitado el cómo codificar sentencias muy largas, hay varias soluciones...

1. Concatenar varias cadenas:

lcSQL = "SELECT campo1, campo2... campoN"
lcSQL = lcSQL+ " WHERE campo1 = condicion1 AND ... "
lcSQL = lcSQL + "blah blah blah"

2. Uso de TEXTMERGE, el cuál, es igual de poderoso, si se le combina con la evaluación de los datos, el tema lo he tocado con anterioridad en el siguiente artículo:

--- TextMerge y SQL Pass Through (SPT) trabajando juntos ---
https://comunidadvfp.blogspot.com/2003/02/textmerge-y-sql-pass-through-spt.html

3. Usando bloques TEXT .. ENDTEXT, en versiones previas a VFP7 este no proporciona la habilidad de evaluar expresiones, pero si nos ofrece el poder generar consultas de gran tamaño y organizadas en un bloque.

TEXT TO lcSQL NOSHOW
   SELECT Campo1, Campo2, Campo3, ..., CampoN
       FROM MiTabla MT INNER JOIN MiOtraTabla MOT 
           ON MiT.MiCampo = MOT.MiCampo
       ....
       ....
ENDTEXT
IF SQLexec(lnHandle, lcSQL, "cCursor") < 0
   IF AERROR(laError) >  0
        Messagebox("Error al ejecutar:" + laError[2])
  ENDIF
ENDIF

A partir de VFP7 se mejoró notablemente a los bloques TEXT ... ENDTEXT, con lo que, puede incluirse evaluación de expresiones...

With Thisform
   lnIdentificador = cResult.iID
   lnMinimo = .txtMinimo.Value
   lnMaximo = .txtMaximo.Value
EndWith
TEXT TO lcSQL TEXTMERGE NOSHOW
   SELECT Campo1, Campo2, Campo3, ..., CampoN
       FROM MiTabla MT INNER JOIN MiOtraTabla MOT 
           ON MiT.MiCampo = MOT.MiCampo
        WHERE iID = < < lnIdentificador > > ;
             AND cantidad BETWEEN < < Minimo > > AND < < lnMaximo > >
       ....
ENDTEXT

En VFP8 se volvió a dar unas pequeñas mejoras que hacen más compatibles con la forma en que se genera el texto resultante, esto se logra gracias a la nueva cláusula PRETEXT.

Todo lo que pongas entre TEXT... ENDTEXT (exceptuando las expresiones a evaluar por el TEXTMERGE) sera ignorado por el compilador de VFP, ya que se presupone que es algún texto que no es necesario compilar ni revisar por errores.

Con el uso de los bloques de TEXT .. ENDTEXT, la antigua "limitación" será llevada hasta más de 16 mil caracteres, ¿te serán suficientes?

Espero que la información te sea de utilidad.

Espartaco Palma Martínez