6 de diciembre de 2010

Liberada la versión Beta de FoxBarcode

Se liberó la versión 0.10 de FoxBarcode, una clase 100% Visual FoxPro que ofrece una herramienta para la generación de imágenes con distintas simbologías de códigos de barras, para ser usadas en informes y formularios de VFP, o exportadas a otras aplicaciones.

NUEVO
FoxBarcode ahora forma parte de VFPx, un proyecto de la Comunidad Mundial de Visual FoxPro para crear complementos de código abierto para Visual FoxPro 9.0.

La página principal del proyecto FoxBarcode en VFPx es: https://github.com/VFPX/FoxBarCode

FoxBarcode

Características

FoxBarcode se puede usar y distribuir libremente por toda la Comunidad de Visual FoxPro.
FoxBarcode soporta las siguientes simbologías de códigos de barras: Código 128, Código 39, Código 39 Extendido (Full ASCII), Código 93, Código 93 Extendido (Full ASCII), Standard 2 de 5, Interleaved 2 de 5, EAN-8, EAN-13, ITF-14, UPC-A, UPC-E, EAN-128 (próximamente), Codabar, Código 11, MSI / Plessey, Telepen y PostNet.

FoxBarcode soporta los siguientes tipos de imagenes: JPG, BMP, GIF, PNG y TIFF.

FoxBarcode genera las imágenes con la clase gpImage2 (http://sites.google.com/site/gpimage2) de Alexander Golovlev (Rusia) y CesarCh (Brasil), basada en GDI+ y requiere gdiplus.dll.

FoxBarcode permite un gran control en la generación de la imagen del código de barras al poder personalizar las siguientes propiedades:
  • nImageHeight: Altura en pixeles de la imagen.
  • nResolution: Resolución de la imagén en DPI.
  • nRotation: Rotación de la imagen. [0=0°, 1=90°, 2=180°, 3=270°]
  • cImageType: Tipo de la imagen generada ["JPG", "GIF", "PNG", "BMP", "TIF"]
  • cImageFile: Nombre del archivo de imagen generado. Si no se especifica, se genera un nombre de archivo aleatorio en la carpeta de archivos temporales de Windows.
  • cText: Texto a codificar.
  • nAlignText: Alineación del texto de la lectura humana. [0=Izquierda, 1=Centro, 2=Derecha]
  • lShowHumanReadableText: .T. si se muestra la lectura humana.
  • lShowCheckDigit: .T. si se muestra el dígito de control en la lectura humana.
  • lShowStartStopChars: .T. si se muestra los caracteres de inicio y final en la lectura humana.
  • cFontName: Nombre de la fuente de la lectura humana.
  • lFontBold: .T. si la fuente de la lectura humana es Negrita.
  • lFontItalic: .T. si la fuente de la lectura humana es Cursiva.
  • nFontSize: Tamaño de la letra de la lectura humana. [Recomendado = 8 ó 9]
  • nFontColor: Color de la fuente de la lectura humana. [Recomendado = Negro = RGB(0,0,0)].
  • nBackColor: Color de fondo de la imagen [Recomendado = Blanco = RGB(255,255,255)].
  • nBarsColor: Color de las barras [Recomendado = Negro = RGB(0,0,0)].
  • nBarcodeType: Tipo de la simbología de código de barra. Vea la lista de codigos de barra soportados y su Id.
  • cSet128 Set: Juego del Código 128. ["A", "B", "C"]
  • cSupplementalText: Texto suplementario en códigos EAN y UPC.
  • lAddCheckDigit: .T. si se calcula el dígito de control. En las simbologías en donde el dígito de control es obligatorio, no se tiene en cuenta esta propiedad.
  • nBearerBar: Tipo del marco portante, solo en la simbología ITF-14. [0=Ninguno, 1=Rectángulo, 2=Superior e inferior]
  • nFactor: Factor de magnificación del código de barras. [1..9]
  • nRatio: Relación entre las barras angostas y las barras anchas en las simbologías que lo permiten.
Para la generación de la imagen, solo se debe llamar al método BarcodeImage() que genera la imagen del código de barras y retorna la ruta y nombre de archivo generado.

FoxBarcode es compatible con todas las versiones de Visual FoxPro a partir de la versión 6.0

Ejemplos

El siguiente es un ejemplo de código para generar una imagen tipo "PNG" del código de barras "Código 128 C" con una altura de "100" pixeles y un factor de magnificación de "2":
*-- Crear el objeto
loFbc = CREATEOBJECT("FoxBarcode")

*-- Configurar las propiedades
WITH loFBC
  .cImageType = "PNG"
  .nBarcodeType = 110 && Code 128
  .cSet128 = "C" && Set 128 C
  .nImageHeight = 100
  .nFactor = 2
ENDWITH

*-- Generar la imagen
lcImagen = loFbc.BarcodeImage("123456789012")
La imagen generada se muestra a continuación

Otras imágenes de ejemplo cambiando solo algunas propiedades de la clase FoxBarcode


Para incluir un código de barras en un formulario, se debe insertar un objeto Image. Como la imagen del código de barras no existe en tiempo de diseño, la propiedad Picture tomará el nombre de la imagen cuando se llame al método BarcodeImage(), por ejemplo desde el método Refresh de Formulario, como se muestra en la siguiente figura:

Para incluir un código de barras en un Informe, se debe insertar un objeto Image y configurar la propiedad ControlSource con un llamado al método BarcodeImage() y se recomienda configurar "Scale contents, retain shape" si la imagen difiere de tamaño con el cuadro.


Importante: Antes de ejecutar el informe y crear el objeto FoxBarcode, se debe declarar la variable como PRIVATE para que ésta tenga alcance en el informe, como se muestra a continuación:
PRIVATE poFbc
poFbc = CREATEOBJECT("FoxBarcode")
...
REPORT FORM My Report

Distribución

Los únicos archivos necesarios que se deben distribuir para que FoxBarcode funcione correctamente son:
  • FoxBarcode.prg
  • gpImage2.prg
  • gpImage2.h
Estos archivos se encuentran en la carpeta \Source incluída en el archivo de la descarga.
Antes de instanciar la clase FoxBarcode, se deben ejecutar las siguientes sentencias:
#INCLUDE "gpImage2.h"
SET PROCEDURE TO LOCFILE("FoxBarcode.prg"), LOCFILE("gpImage2.prg") ADDITIVE

Demo

En los archivos de la descarga, está incluido un proyecto completo de una aplicación Demo que utiliza FoxBarcode. La siguiente es una captura de pantalla de la aplicación.



La aplicación permite:
  • Configurar todas las propiedades de la clase FoxBarcode y muestrar la imagen generada automáticamente.
  • Copiar al portapapeles la imagen generada.
  • Guardar en un archivo la imagen.
  • Imprimir el código de barras.
  • Ejecutar diversos informes y formularios de ejemplo.
  • Construir un Script con el código en Visual FoxPro
En la aplicación también se muestra una breve descripción de cada simbología de códigos de barras, según se seleccione en la lista desplegable de las simbologías.

Descargas

Para descargar la última versión de FoxBarcode, visite la página de Descargas de FoxBarcode haciendo clic aquí.

Feedback

FoxBarcode se encuentra en fase Beta, por lo que cualquier comentario será bienvenido. Los comentarios, como así también los reportes de incidencias usando la clase FoxBarcode se deben enviar por correo electrónico a vfpencoding@gmail.com

¿Quiénes somos?

VFPEncoding es un grupo de desarrolladores pertenecientes a la Comunidad Hispana de Visual FoxPro que creo FoxBarcode. Los miembros de VFPEncoding son:
  • Guillermo Carrero (Barcelona, España)
  • Luis María Guayán (Tucumán, Argentina)
Agradecimientos
  • A nuestras familias.
  • Cesar Ch. por sus continuas ideas y colaboraciones con sus clases gpImage2 y FoxyPreviewer.

20 de septiembre de 2010

¿Qué es una clave?

Articulo original: What is a Key?
http://weblogs.foxite.com/andykramek/archive/2008/09/03/6656.aspx
Autor: Andy Kramek
Traducido por: Luis María Guayán

La palabra "clave" tiene un sentido muy particular en el diseño de bases de datos relacionales. Se refiere a un campo cuyo valor está compartido entre las tablas. El propósito de esta duplicación de datos es permitir un registro en una tabla esté asociado con un registro a otra, lo que permite la extracción de información que está distribuida entre las tablas. Esta asociación se denomina como una "relación" y es lo que diferencia las bases de datos relacionales, de las bases de datos de archivos planos.

Hay dos tipos de claves relacionales:
  • PRIMARIA (PRIMARY) Una clave primaria es la columna, o una combinación de columnas, cuyo valor identifica de forma exclusiva un único registro dentro de una tabla. Ni la clave primaria, ni ninguna parte de ella, puede tener un valor NULO
  • FORANEA (FOREING) Una clave foránea es la columna, o una combinación de columnas en una tabla cuyo valor identifica a un solo (es decir, relacionado) registro de otra tabla. Una clave foránea puede, bajo algunas circunstancias, tener un valor NULO que indica que no hay registros relacionados.
Cuando definimos una relación entre dos tablas, la tabla que contiene la clave primaria es la "Tabla Padre" (también conocida como "tabla de referencia") y la tabla que contiene la clave foránea es la "Tabla Hija" (también conocida como el "tabla referenciada"). Por lo tanto, la clave foránea hace referencia a la primaria, y por ende, define cómo los registros en las tablas están relacionados entre sí.

Es una regla fundamental que todas las tablas deben tener una clave primaria. Esto es evidente si consideramos una tabla que contiene los nombres verdaderos de mis familiares directos que se enumeran a continuación.
Nombre      Apellido
Stanley     Kramek  
Elaine      Kramek  
Andrew      Kramek  
Richard     Kramek  
Angela      Kramek  
Esto está bien y muestra mi padre (Stanley) y mi madre (Elaine), junto con mi hermano menor (Richard) y mi hermana (Angela). La clave primaria de esta tabla es la combinación de nombre-apellido y es perfectamente permitido. Sin embargo, no es un buen diseño, porque cuando mi hermano se casó con una muchacha llamada "Angela" (y sí, realmente así fue) nos encontramos con un problema cuando intentamos agregar a mi nueva cuñada a esta tabla:
Nombre      Apellido
Stanley     Kramek  
Elaine      Kramek  
Andrew      Kramek  
Richard     Kramek  
Angela      Kramek  
Angela      Kramek  
Ahora no tenemos manera de distinguir entre mi hermana y mi cuñada! Esta tabla ya no tiene una clave primaria y en ausencia de cualquier información adicional es realmente inútil. Por supuesto, la solución obvia es añadir algo adicional, diferente, tal vez la "inicial" del segundo nombre, a pesar de que esta no es una buena opción, sobre todo porque no todo el mundo tiene uno, y el valor aún podría no ser único, y en este caso no lo es (el segundo nombre de mi hermano es "Maurice", el de mi hermana es "Mary" y el de mi cuñada es "Margaret").
Nombre      Apellido       Inicial
Stanley     Kramek    
Elaine      Kramek    
Andrew      Kramek         E
Richard     Kramek         M
Angela      Kramek         M
Angela      Kramek         M
El punto de este ejemplo, es que es sumamente importante definir una clave primaria para cada tabla, de forma de que cada registro pueda ser único e identificado inequívocamente. Hasta ahora hemos definido la clave primaria como una combinación de columnas que se utilizan para almacenar los datos. Esto se conoce como una clave "compuesta".

30 de agosto de 2010

Formas de conectarse a un Servidor SQL

La primera, según mi experiencia, es la forma mas rápida de conectarse, también depende como esten planteando el sistema. Ésta al ser la "mas rápida", puede usarse para que cada vez haga una consulta.
La segunda puede plantearse al iniciar el programa, y las consultas se hacen sobre esa conexión pero no se vuelve a crear por cada consulta

La tercera, igual que la segunda, la ventaja de esta es que no es necesario declarla en ODBC

La primera
***************************
oConn  = createobject('ADODB.Connection')
cConnString="Provider=SQLOLEDB.1;Persist Security Info=False;User ID="+ALLTRIM(lc_usuario_)+";Pwd="+ALLTRIM(lc_password)+";Initial Catalog="+ALLTRIM(lc_nombre)+";Data Source="+ALLTRIM(lc_sname)
oConn.Open(cConnString)
oRS = CREATEOBJECT("ADODB.Recordset")
oRS.ActiveConnection = oConn
oCA=CREATEOBJECT("CursorAdapter")
oCA.DataSourceType = "ADO"
oCA.DataSource = oRS
oCA.MapBinary = .T.
oCA.MapVarchar = .T.
oCA.SelectCmd = "select * from empleados"  && ejemplo
IF !oCA.CursorFill()
  LOCAL laError
  DIMENSION laError[1]
  AERROR(laError)
  MESSAGEBOX(laError[2])
ELSE
  ********** lo "capturo" en un cursor para manipularlo en modo local
  loAlias=ALIAS()
  cMacro1="SELECT * FROM "+loAlias+" INTO CURSOR "+ loCursor +" readwrite"
  &cMacro1
  WAIT windows "Procesado "+loCursor NOWAIT
ENDIF 
*****************************************************************

La segunda, Atravez de ODBC
lc_sql=SQLCONNECT("localdb","simio","fantasiA") && localdb tiene que estar creado en odbc solapa usuarios
if m.lc_con < 0
  nopc=MESSAGEBOX("No se pudo conectar , talvez ODBC no este configurado",0+48,"Error")
  RETURN .f.
endif 
****************************************

La tercera, conectar usando los drivers de ODBC (no es necesario crear una conexion en ODBC)
lcStringConn="Driver={SQL Server}"+";Server="+lc_IPSQL+";Database="+lc_nombre+";Uid="+lc_usuario_+";Pwd="+lc_password
***Evitar que aparezca  la ventana de login 
SQLSETPROP(0,"DispLogin",3)
SQLSETPROP(0,"IdleTimeout",0)
lc_con=SQLSTRINGCONNECT(lcStringConn)
if m.lc_con < 0  &&  nose pudo conectar
  wait windows "no conecta"
endif 
****************************************

Carlos [caremi66]

28 de julio de 2010

Autogenerar Report mejorando presentacion

En muchas ocasiones me surgía la necesidad de realizar un listado sencillo, a partir de una pequeña tabla o cursor, lo que me obligaba a diseñar e incluir en mis proyectos, numerosos 'reports', cuya utilidad no era excesivamente importante.

Siempre había descartado la opcion "CREATE REPORT FROM 'filename' ", porque el resultado tenía una apariencia demasiado rustica.

Todo esto me terminó por decidir a intentar desarrollar una rutina que, tras generar un report automático desde una tabla o cursor, mejorase algo su presentación. No pretendo obtener un resultado espectacular ni incluir un monton de opciones y posibilidades, sino solo conseguir un report con un aspecto presentable.

A la rutina siguiente, solo es necesario pasarle 2 parametros, el nombre de la tabla o cursor, a partir de la cual se generará el report, y un título para el listado.

Los campos se presentarán en el orden en que aparezcan en la tabla o cursor.

Esta es la rutina:
**************************************************************
* Autogeneracion de un listado (report rapido) partiendo de un
* cursor o tabla.
* Se autoajusta fuente, tamaño y nº de columnas a mostrar
*
* Parametros: cCursor --> Nombre del cursor/tabla origen
*                    cTitulo --> Titulo para el listado
*
* Ej: do autorepo with "micursor", "Titulo del Listado"
*
****************************************************************
LPARAMETERS cCursor, cTitulo
*
LOCAL cRepo, nLong, sFont, cFont, cCampos, nMaxCol, sExtra

cRepo = SYS(2015) + '.frx'
*
IF VARTYPE(cCursor) # 'C'
  WAIT WINDOW "Faltan Datos"
ENDIF
IF VARTYPE(cTitulo) # 'C'
  cTitulo = ''
ENDIF

cFont = 'Calibri'  && Fuente de letra a usar
nMaxCol = 225      && Nº máximo de columnas (depende de fuente)
nLong = 0
cCampos = ''

* Establecer anchura, limites y tamaño fuente
nCampos = AFIELDS(aCampos, cCursor)
FOR xx = 1 TO nCampos
  nLong = nLong + aCampos(xx, 3)
  IF nLong > nMaxCol
    cTitulo = cTitulo + '    ***'
    EXIT
  ELSE
    IF EMPTY(cCampos)
      cCampos = cCampos + aCampos(xx,1)
    ELSE
      cCampos = cCampos + ', ' +aCampos(xx,1)
    ENDIF
  ENDIF
ENDFOR

* Ajusto tamaño de fuente depende long. datos
* 'sExtra' para ajustar campos (depende de ver. VFP)
DO CASE
  CASE nLong > 190
    sFont = 7
    sExtra = IIF(VERSION(5) / 100 = 9, 120, -320)

  CASE nLong > 160
    sFont = 8
    sExtra = IIF(VERSION(5) / 100 = 9, 200, -170)

  CASE nLong > 135
    sFont = 9
    sExtra = IIF(VERSION(5) / 100 = 9, 360, -110)

  OTHERWISE
    sFont = 10
    sExtra = IIF(VERSION(5) / 100 = 9, 450, 180)
ENDCASE

* Crear un report y pasar a realizar ajustes
CREATE REPORT (cRepo) FROM DBF(cCursor) FIELDS &cCampos COLUMN WIDTH 256
*
USE (cRepo) IN 0 ALIAS mirepor EXCL
SELECT mirepor
*
* Cambiar texto 'Page' por 'Página' en caso de runtime en ingles
REPLACE EXPR WITH ["Página "] FOR ALLTRIM(EXPR) = ["Page "] IN mirepor
*
* Cambiar la fuente para todos 'labels' y 'campos'
REPLACE ALL fontface WITH cFont FOR INLIST(objtype, 5, 8)

* Cambiar tamaño fuente y estilo para 'labels' encabezado columnas
REPLACE ALL FONTSIZE WITH sFont, fontstyle WITH 3  FOR objtype=5 AND Vpos=0 IN mirepor

* Reducir tamaño fuente para todos 'campos'
REPLACE ALL FONTSIZE WITH sFont - 1  FOR objtype = 8

* Cambiar tamaño fuente y estilo para 'labels' y 'campos' del pie de pagina
REPLACE FONTSIZE WITH sFont - 1, fontstyle WITH 2 FOR ALLTRIM(EXPR) = [DATE()] IN mirepor
REPLACE FONTSIZE WITH sFont - 1, fontstyle WITH 2 FOR ALLTRIM(EXPR) = ["Página "] IN mirepor
REPLACE FONTSIZE WITH sFont - 1, fontstyle WITH 2 FOR ALLTRIM(EXPR) = [_PAGENO] IN mirepor
*
* Añadir línea separación en pie de pagina
GOTO TOP
LOCATE FOR ALLTRIM(mirepor.EXPR) = [_PAGENO]
miW = mirepor.hpos + mirepor.WIDTH + 100
miV = mirepor.Vpos -100
*
APPEND BLANK IN mirepor
REPLACE mirepor.objtype WITH 6
REPLACE mirepor.Vpos WITH miV
REPLACE mirepor.WIDTH WITH miW
REPLACE mirepor.HEIGHT WITH 105
REPLACE mirepor.penpat WITH 8
REPLACE mirepor.supalways WITH .T.
REPLACE mirepor.platform WITH 'WINDOWS'
*
* Añadir línea separación en encabezado
GOTO TOP
LOCATE FOR mirepor.objtype=5 AND mirepor.Vpos=0
*
miV = mirepor.Vpos + mirepor.HEIGHT
*
APPEND BLANK IN mirepor
REPLACE mirepor.objtype WITH 6
REPLACE mirepor.Vpos WITH miV
REPLACE mirepor.WIDTH WITH miW
REPLACE mirepor.HEIGHT WITH 105
REPLACE mirepor.penpat WITH 8
REPLACE mirepor.supalways WITH .T.
REPLACE mirepor.platform WITH 'WINDOWS'
*
* Mover todo hacia abajo, para colocar titulo
IF !EMPTY(cTitulo)
  *
  extra = 4000 && Altura para el titulo
  GOTO TOP
  REPLACE ALL Vpos WITH Vpos + extra FOR INLIST(objtype, 5, 6, 8) IN mirepor
  REPLACE ALL HEIGHT WITH HEIGHT + extra FOR objcode = 1 IN mirepor
  *
  * Añadir Titulo
  APPEND BLANK IN mirepor
  REPLACE mirepor.platform WITH 'WINDOWS'
  REPLACE mirepor.objtype WITH 5
  REPLACE mirepor.hpos WITH 100
  REPLACE mirepor.fontface WITH cFont
  REPLACE mirepor.fontstyle WITH 4
  REPLACE mirepor.FONTSIZE WITH 16
  REPLACE mirepor.WIDTH WITH 70000
  REPLACE mirepor.HEIGHT WITH 2800
  REPLACE mirepor.supalways WITH .T.
  REPLACE mirepor.EXPR WITH ["&cTitulo"]
  REPLACE mirepor.mode WITH 1
  *
ENDIF
*
* Ajustar 'labels' columnas segun version VFP
REPLACE ALL mirepor.Vpos WITH mirepor.Vpos - sExtra FOR mirepor.objtype=5 AND mirepor.Vpos = extra
*
DELETE ALL FOR objtype = 26 IN mirepor
PACK
*
USE IN mirepor
*
* Mandar impresion
oForm = CREATEOBJECT("Form")
WITH oForm
  .CAPTION = "Vista Previa "
  .WINDOWTYPE = 1
  .WIDTH = _SCREEN.WIDTH - 16
  .HEIGHT = _SCREEN.HEIGHT - 16
  *
  SELECT &cCursor
  GOTO TOP
  REPORT FORM (cRepo) PREVIEW WINDOW (.NAME)
  REPORT FORM (cRepo) TO PRINTER PROMPT NOCONSOLE NOEJECT
  *
  .RELEASE()
ENDWITH
*
* Borrar Report autogenerado
DELETE FILE (JUSTSTEM(cRepo) + '.frx')
DELETE FILE (JUSTSTEM(cRepo) + '.frt')
*
RETURN
Jose Antonio Blasco

17 de julio de 2010

Calculo de cuotas por el sistema francés

Con esta rutina podemos calcular el valor de la cuota, cuota capitalizada e interés según el sistema francés.
*-- Sistema Frances 
*-- Datos a Cargar 
Capital = 10000 
CantidadCuotas = 36 
TasaAnual = 22  && Porcentaje 
TasaMensual = TasaAnual/12/100 
*-- Listado 
? "    Nro.Cuota        Cuota             Cuota Capitalizada             Interes" 
? REPLICATE("-",80) 
FOR NroCuota = 1 TO CantidadCuotas 
  Cuota = Capital * (TasaMensual/(1-(1+TasaMensual)^-CantidadCuotas)) 
  CuotaCapitalizada = Cuota * (1-TasaMensual*(1-(1+TasaMensual)^-(CantidadCuotas-NroCuota+1))/TasaMensual ) 
  Interes = TasaMensual * Cuota * (1-(1+TasaMensual)^-(CantidadCuotas-NroCuota+1))/TasaMensual 
  ? NroCuota, ROUND(Cuota,2), ROUND(CuotaCapitalizada,2), ROUND(Interes,2) 
ENDFOR 

Luis María Guayán

26 de mayo de 2010

Un sencillo método para validar que no se dejen determinados datos vacíos en un formulario.

Primeramente coloca un * (asterisco) en la propiedad Comment a los objetos que deseas validar.

Posteriormente coloca el siguiente código en el borón Grabar del formulario.

LOCAL lnObjecto,lcContenido,lcNombre,lcFoco
FOR lnObjecto = 1 TO THISFORM.OBJECTS.COUNT
  IF THISFORM.OBJECTS(lnObjecto).COMMENT = "*"
    lcNombre = THISFORM.OBJECTS(lnObjecto).NAME
    lcContenido = "ThisForm." + lcNombre + ".Value"
    IF EMPTY(&lcContenido)
      MESSAGEBOX("Es necesario capturar el dato " + ;
        SUBSTR(lcNombre,4), 16, "No es posible")
      lcFoco = "ThisForm." + lcNombre + ".SetFocus"
      &lcFoco
      RETURN
    ENDIF
  ENDIF
ENDFOR

Saludos.

Jesus Caro V.

6 de abril de 2010

Menú desplegable en un CommandButton

Esta es una clase simple para mostrar un menú desplegable bajo un CommandButton. El menú se muestra al hacer click sobre el botón, o pulsando la barra espaciadora, F4 o ALT+DOWN sobre el mismo.

Espero les sea de utilidad
* Menú en commandbutton
WITH NEWOBJECT("xForm")
  .SHOW(1)
ENDWITH
RETURN

DEFINE CLASS xForm AS FORM
  ADD OBJECT cmdMenu1 AS xCommandButton WITH ;
    CAPTION = "Menu 1", TOP = 10, LEFT = 10,;
    MenuOptions = "Opcion 1 . \<1,Opcion 1 . \<2,Opcion 1 . \<3"

  ADD OBJECT cmdMenu2 AS xCommandButton WITH ;
    CAPTION = "Menu 2", TOP = 10, LEFT = 120,;
    MenuOptions = "Opcion 2 . \<1,Opcion 2 . \<2"

  ADD OBJECT cmdClose AS xCommandButton WITH ;
    CANCEL = .T., CAPTION = "Cerrar", TOP = 10, LEFT = 240

  PROCEDURE cmdClose.CLICK
    THISFORM.HIDE()
  ENDPROC
ENDDEFINE

DEFINE CLASS xCommandButton AS COMMANDBUTTON
  HEIGHT = 25

  MenuOptions = ""

  PROCEDURE MOUSEMOVE (nButton, nShift, nXCoord, nYCoord)
    IF nButton==1 AND ! EMPTY(THIS.MenuOptions)
      THIS.ShowPopupMenu()
      NODEFAULT
    ENDIF
  ENDPROC

  PROCEDURE KEYPRESS(nKeyCode, nShiftCtrlAlt)
    #DEFINE K_DOWN 160
    #DEFINE K_ALT 4
    #DEFINE K_F4 -3

    * WAIT WINDOW TRANSFORM(nKeyCode) + " " + TRANSFORM(nShiftCtrlAlt) NOWAIT

    DO CASE
      CASE nKeyCode==K_DOWN AND nShiftCtrlAlt==K_ALT
        NODEFAULT
        THIS.ShowPopupMenu()
      CASE nKeyCode==K_F4
        NODEFAULT
        THIS.ShowPopupMenu()
    ENDCASE
  ENDPROC

  PROCEDURE CLICK
    NODEFAULT
    THIS.ShowPopupMenu()
  ENDPROC

  * Armo y muestro el menú
  PROCEDURE ShowPopupMenu()
    LOCAL nOp, sOp, nY, nX, nRatio

    TRY
      RELEASE POPUP _Popup_Menu
    CATCH
    ENDTRY

    WITH THIS
      THISFORM.ADDPROPERTY("ActivePopupMenu", THIS)

      * Convierto de pixels a foxels
      nRatio = FONTMETRIC(1)
      nY = (.TOP + .HEIGHT) / nRatio

      nRatio = FONTMETRIC(6)
      nX = .LEFT / nRatio

      DEFINE POPUP _Popup_Menu FROM nY, nX SHORTCUT

      FOR nOp = 1 TO GETWORDCOUNT(.MenuOptions, ",")
        sOp = GETWORDNUM(.MenuOptions, nOp, ",")
        DEFINE BAR (nOp) OF _Popup_Menu PROMPT (sOp)
        sOp = TRANSFORM(nOp)
        ON SELECTION BAR (nOp) OF _Popup_Menu _SCREEN.ACTIVEFORM.ActivePopupMenu.AfterClick( &sOp )
      NEXT
    ENDWITH

    ACTIVATE POPUP _Popup_Menu
  ENDPROC

  * Borro el menú luego del click y llamo al handler
  PROCEDURE AfterClick(nOption)
    THISFORM.ActivePopupMenu = NULL
    * RELEASE POPUP _Popup_Menu
    THIS.OnMenu (nOption)
    THISFORM.REFRESH()
  ENDPROC

  PROCEDURE OnMenu (nOption)
    WAIT WINDOW "Seleccionó opcion " + TRANSFORM(nOption) + " del botón " + PROPER(THIS.NAME) NOWAIT
  ENDPROC
ENDDEFINE

Mario Lopez

5 de febrero de 2010

Un reemplazo para FULLPATH()

Artículo original: A Replacement for FULLPATH()
http://doughennig.blogspot.com/2010/02/replacement-for-fullpath.html)
Autor: Doug Hennig
Traductor: Luis María Guayán

¿Está tan molesto como yo de que FULLPATH() devuelve la ruta completa de un archivo en mayúsculas? Esto hace un poco difícil, respetar las mayúsculas y minúsculas en un nombre de archivo introducido por el usuario. Afortunadamente, la función GetFullPathName de la API de Windows no cambia las mayúsculas y minúsculas. Aquí hay una pequeña función que recibe un nombre de archivo y devuelve la ruta completa usando la función de la API:
LPARAMETERS tcName
LOCAL lcBuffer1, ;
  lcBuffer2, ;
  lnLen

#DEFINE MAX_PATH 260

DECLARE LONG GetFullPathName IN Win32API ;
  STRING lpFileName, LONG nBufferLength, STRING @lpBuffer, ;
  STRING @lpFilePart

STORE SPACE(MAX_PATH) TO lcBuffer1, lcBuffer2
lnLen = GetFullPathName(tcName, MAX_PATH, @lcBuffer1, @lcBuffer2)
RETURN LEFT(lcBuffer1, lnLen)

18 de enero de 2010

Saber cuantos decimales tiene un número

Con esta función podemos saber la cantidad de decimales de un número pasado como parámetro.

? CuantosDecimales(1234.567) 
? CuantosDecimales(1234.500) 
? CuantosDecimales(9.999999) 

FUNCTION CuantosDecimales(tn) 
  LOCAL ln
  ln = 0 
  DO WHILE .T. 
    IF INT(tn * 10 ^ ln) == tn * 10 ^ ln 
      EXIT 
    ENDIF 
    ln = ln + 1 
  ENDDO 
  RETURN ln 
ENDFUNC 

Nota: La función solo soporta hasta 16 decimales

Luis María Guayán

9 de enero de 2010

Poner la imagen del papel tapiz de Windows como fondo de un formulario

Una forma fácil de poner la misma imagen del papel tapiz de Windows como fondo de un formulario.

Para ello leemos el valor de la Registry: HKCU\Control Panel\Desktop\Wallpaper con WSH (Windows Script Host)

Ejemplo:
LOCAL loForm, loWsh, lcWallpaper

loWsh = CreateObject("wscript.shell")  
lcWallpaper = loWsh.RegRead("HKCU\Control Panel\Desktop\Wallpaper")
loWsh = Null

loForm = CREATEOBJECT("Form")
loForm.WIDTH = 800
loForm.HEIGHT = 600
loForm.AUTOCENTER = .T.
loForm.PICTURE = lcWallpaper
loForm.SHOW(1)
loForm = Null

RETURN

Luis María Guayán

1 de enero de 2010

Visual FoxPro 9.0 EULA (End-User License Agreement)

Contrato de Licencia con el Usuario Final de Visual FoxPro 9.0

LICENSE TERMS FOR MICROSOFT SOFTWARE

MICROSOFT VISUAL FOXPRO VERSION 9.0

THESE LICENSE TERMS ARE AN AGREEMENT BETWEEN MICROSOFT CORPORATION (OR, IF APPLICABLE BASED ON WHERE YOU ACQUIRED THE SOFTWARE, ONE OF ITS AFFILIATES) AND YOU.  PLEASE READ THEM.  THEY APPLY TO THE SOFTWARE NAMED ABOVE WHICH INCLUDES THE MEDIA ON WHICH YOU RECEIVED IT, IF ANY.  THE TERMS ALSO APPLY TO THE MICROSOFT:

* UPDATES, 
* SUPPLEMENTS,
* INTERNET-BASED SERVICES, AND
* SUPPORT SERVICES,

FOR THIS SOFTWARE UNLESS OTHER TERMS ACCOMPANY THOSE ITEMS.  IF SO, THOSE TERMS APPLY.