Les dejo el ejemplo refactorizado de : C:\hmg.3.6\SAMPLES\Applications\AGENDA\Agenda.prg
Code: Select all
/*
* Agenda de Contatos (2)
* Humberto Fornazier - Março/2003
* hfornazier@brfree.com.br
*
* HMG - Harbour Win32 GUI library - Release 60
* Copyright 2002 Roberto Lopez <mail.box.hmg@gmail.com>
* http://www.hmgforum.com//
*
* Refactored to work with JSON instead of DBF files
* Refactorizado para trabajar con JSON en lugar de archivos DBF
*/
#include "hmg.ch"
#define BLUE { 0, 0, 128 }
PROCEDURE Main()
LOCAL i
// Codepage setting for console and GUI / Configuración de codepage
hb_cdpSelect( "UTF8" )
// Public global variable for memory DOM / Variable global pública para hDOM
PUBLIC hDOM
hDOM := {=>}
// Initialize or load JSON agenda / Inicializar o cargar la agenda JSON
CargarAgendaJSON( "agenda.json" )
PRIVATE lNovo := .F.
DEFINE WINDOW Form_1 ;
AT 0,0 ;
WIDTH 480 ;
HEIGHT 470 ;
TITLE "Agenda de Contactos / Contacts Agenda" ;
MAIN ;
NOMAXIMIZE ;
NOSIZE ;
ON RELEASE GuardarAgendaJSON( "agenda.json" ) ;
BACKCOLOR BLUE
// Index column (A-Z search) / Columna de índice (Búsqueda A-Z)
@ 010,415 GRID GIndice OF Form_1 ;
WIDTH 48 HEIGHT 360 ;
HEADERS {""} WIDTHS { 28 } ;
FONT "Arial" SIZE 09 BOLD ;
TOOLTIP "Click on desired letter / Click en la letra deseada" ;
ON CLICK Pesquisa_Agenda()
// Main contacts Grid / Grilla principal de contactos
@ 010,010 GRID Grid_Agenda ;
WIDTH 398 ;
HEIGHT 360 ;
HEADERS {"Código / Code", "Nome / Name"} ;
WIDTHS { 80, 290 } ;
FONT "Arial" SIZE 09 ;
ON DBLCLICK Novo_Registro( .F. )
// Action Buttons / Botones de acción
@ 385,010 BUTTON Btn_Novo OF Form_1 ;
CAPTION '&Novo / New' ;
ACTION Novo_Registro( .T. ) ;
WIDTH 120 HEIGHT 27 ;
FONT "Arial" SIZE 09 ;
TOOLTIP "New Record / Novo Registro" ;
FLAT
@ 385,165 BUTTON Btn_Imprimir OF Form_1 ;
CAPTION '&Imprimir / Print' ;
ACTION Imprimir() ;
WIDTH 120 HEIGHT 27 ;
FONT "Arial" SIZE 09 ;
TOOLTIP "Print Contacts / Imprimir Contatos" ;
FLAT
@ 385,318 BUTTON Btn_Sair OF Form_1 ;
CAPTION '&Sair / Exit' ;
ACTION Form_1.Release ;
WIDTH 120 HEIGHT 27 ;
FONT "Arial" SIZE 09 ;
TOOLTIP "Exit System / Finalizar Sistema" ;
FLAT
END WINDOW
// Populate A-Z index grid / Poblar el índice A-Z
FOR i := 1 TO 26
ADD ITEM { CHR( i + 64 ) } TO GIndice OF Form_1
NEXT
MODIFY CONTROL GIndice OF Form_1 VALUE 1
Pesquisa_Agenda()
CENTER WINDOW Form_1
ACTIVATE WINDOW Form_1
RETURN
// ----------------------------------------------------------------------
// Filter contacts by selected letter in hDOM
// Filtrar contactos en hDOM según la letra seleccionada
// ----------------------------------------------------------------------
FUNCTION Pesquisa_Agenda()
LOCAL cPesq := ValorDaColuna( "GIndice", "Form_1", 1 )
LOCAL cIdKey, hContacto, cNome
cPesq := IIf( Empty( cPesq ), "A", Upper( cPesq ) )
DELETE ITEM ALL FROM Grid_Agenda OF Form_1
IF hb_HHasKey( hDOM, "agenda" ) .AND. hb_HHasKey( hDOM["agenda"], "contactos" )
FOR EACH cIdKey IN hDOM["agenda"]["contactos"]:Keys
hContacto := hDOM["agenda"]["contactos"][ cIdKey ]
cNome := Upper( hb_HGetDef( hContacto, "name", "" ) )
IF Left( cNome, 1 ) == cPesq
ADD ITEM { cIdKey, hContacto["name"] } TO Grid_Agenda OF Form_1
ENDIF
NEXT
ENDIF
RETURN Nil
// ----------------------------------------------------------------------
// Open New / Edit Contact Form
// Abrir Formulario de Nuevo / Editar Contacto
// ----------------------------------------------------------------------
FUNCTION Novo_Registro( lNovo_Registro )
LOCAL cCodigo := ""
LOCAL cNome := ""
LOCAL cFone1 := ""
LOCAL cEmail := ""
LOCAL hContacto
Form_1.Btn_Novo.Enabled := .F.
Form_1.Btn_Sair.Enabled := .F.
lNovo := lNovo_Registro
IF !lNovo
cCodigo := ValorDaColuna( "Grid_Agenda", "Form_1", 1 )
IF Empty( cCodigo ) .OR. !hb_HHasKey( hDOM["agenda"]["contactos"], cCodigo )
MsgSTOP( "Record " + cCodigo + " not found! / Registro " + cCodigo + " no localizado!", "Agenda" )
Form_1.Btn_Novo.Enabled := .T.
Form_1.Btn_Sair.Enabled := .T.
RETURN Nil
ENDIF
hContacto := hDOM["agenda"]["contactos"][ cCodigo ]
cNome := AllTrim( hb_HGetDef( hContacto, "name", "" ) )
cFone1 := AllTrim( hb_HGetDef( hContacto, "phone", "" ) )
cEmail := AllTrim( hb_HGetDef( hContacto, "email", "" ) )
ENDIF
DEFINE WINDOW Form_2 ;
AT 0,0 ;
WIDTH 490 HEIGHT 230 ;
TITLE "Agenda - " + IIf( lNovo, "New Record / Nuevo Registro", "Editing / Editando " + cCodigo ) ;
MODAL NOSIZE ;
ON RELEASE {|| Form_1.Btn_Novo.Enabled := .T., Form_1.Btn_Sair.Enabled := .T., Pesquisa_Agenda(), Form_1.Grid_Agenda.SetFocus() } ;
BACKCOLOR WHITE
@ 10,10 LABEL Label_Codigo VALUE 'Código / Code' WIDTH 140 HEIGHT 25 FONT 'Arial' SIZE 09 BACKCOLOR WHITE FONTCOLOR BLUE BOLD
@ 40,10 LABEL Label_Nome VALUE 'Nome / Name' WIDTH 140 HEIGHT 25 FONT 'Arial' SIZE 09 BACKCOLOR WHITE FONTCOLOR BLUE BOLD
@ 70,10 LABEL Label_Fone1 VALUE 'Fone / Phone' WIDTH 140 HEIGHT 25 FONT 'Arial' SIZE 09 BACKCOLOR WHITE FONTCOLOR BLUE BOLD
@ 100,10 LABEL Label_Email VALUE 'e-mail' WIDTH 140 HEIGHT 25 FONT 'Arial' SIZE 09 BACKCOLOR WHITE FONTCOLOR BLUE BOLD
@ 10,120 TEXTBOX T_Codigo VALUE cCodigo WIDTH 60 TOOLTIP 'Code / Código'
@ 40,120 TEXTBOX T_Nome OF Form_2 WIDTH 330 VALUE cNome TOOLTIP 'Name / Nombre' MAXLENGTH 40 ON ENTER Form_2.T_Fone1.SetFocus
@ 70,120 TEXTBOX T_Fone1 OF Form_2 WIDTH 180 VALUE cFone1 TOOLTIP 'Phone / Teléfono' MAXLENGTH 20 ON ENTER Form_2.T_Email.SetFocus
@ 100,120 TEXTBOX T_Email OF Form_2 WIDTH 330 VALUE cEmail TOOLTIP 'E-mail' MAXLENGTH 40 LOWERCASE ON ENTER Form_2.Btn_Salvar.SetFocus
@ 150,40 BUTTON Btn_Salvar OF Form_2 CAPTION '&Salvar / Save' ACTION Salvar_Registro() WIDTH 120 HEIGHT 27 FONT "Arial" SIZE 09 FLAT
@ 150,180 BUTTON Btn_Excluir OF Form_2 CAPTION '&Deletar / Delete' ACTION Excluir_Registro() WIDTH 120 HEIGHT 27 FONT "Arial" SIZE 09 FLAT
@ 150,320 BUTTON Btn_Cancelar OF Form_2 CAPTION '&Cancelar / Cancel' ACTION Form_2.Release WIDTH 120 HEIGHT 27 FONT "Arial" SIZE 09 FLAT
END WINDOW
Form_2.T_Codigo.Enabled := .F.
IF lNovo
Form_2.Btn_Excluir.Enabled := .F.
ENDIF
CENTER WINDOW Form_2
ACTIVATE WINDOW Form_2
RETURN Nil
// ----------------------------------------------------------------------
// Save contact to hDOM memory structure
// Guardar contacto en la estructura hDOM en memoria
// ----------------------------------------------------------------------
FUNCTION Salvar_Registro()
LOCAL cCodigo := ""
LOCAL hNovoContacto
IF Empty( Form_2.T_Nome.Value )
MsgINFO( "Name not specified! / Nome não foi Informado!!", "Agenda" )
Form_2.T_Nome.SetFocus
RETURN Nil
ENDIF
IF lNovo
hDOM["agenda"]["ultimo_id"]++
cCodigo := LTrim( Str( hDOM["agenda"]["ultimo_id"] ) )
ELSE
cCodigo := Form_2.T_Codigo.Value
ENDIF
hNovoContacto := {=>}
hNovoContacto["id"] := Val( cCodigo )
hNovoContacto["name"] := Form_2.T_Nome.Value
hNovoContacto["phone"] := Form_2.T_Fone1.Value
hNovoContacto["email"] := Form_2.T_Email.Value
// Save/Update in hDOM Hash Map
hDOM["agenda"]["contactos"][ cCodigo ] := hNovoContacto
// Save directly to JSON disk file
GuardarAgendaJSON( "agenda.json" )
MsgINFO( "Record " + IIf( lNovo, "Saved / Incluído", "Updated / Alterado!" ), "Agenda" )
PosicionaIndice( Upper( Left( Form_2.T_Nome.Value, 1 ) ) )
Form_2.Release
RETURN Nil
// ----------------------------------------------------------------------
// Delete contact from hDOM and update JSON
// Eliminar contacto de hDOM y actualizar JSON
// ----------------------------------------------------------------------
FUNCTION Excluir_Registro()
LOCAL cCodigo := Form_2.T_Codigo.Value
IF MsgOkCancel( "Confirm record deletion? / Confirma Exclusão do Registro??", "Delete / Excluir" )
IF hb_HHasKey( hDOM["agenda"]["contactos"], cCodigo )
// Delete key from Hash Map
hb_HDel( hDOM["agenda"]["contactos"], cCodigo )
// Update JSON file
GuardarAgendaJSON( "agenda.json" )
MsgINFO( "Record deleted! / Registro Excluído!!", "Agenda" )
Form_2.Release
ENDIF
ENDIF
RETURN Nil
// ----------------------------------------------------------------------
// Load JSON or initialize default hDOM structure
// Cargar JSON o inicializar estructura hDOM por defecto
// ----------------------------------------------------------------------
FUNCTION CargarAgendaJSON( cNombreArchivo )
LOCAL cContenido := ""
LOCAL hResultado := {=>}
IF hb_FileExists( cNombreArchivo )
cContenido := hb_MemoRead( cNombreArchivo )
IF !Empty( cContenido )
hb_jsonDecode( cContenido, @hResultado )
IF !Empty( hResultado )
hDOM := hResultado
RETURN hDOM
ENDIF
ENDIF
ENDIF
// Default structure if file does not exist / Estructura por defecto si no existe
hDOM := {=>}
hDOM["agenda"] := {=>}
hDOM["agenda"]["contactos"] := {=>}
hDOM["agenda"]["ultimo_id"] := 0
RETURN hDOM
// ----------------------------------------------------------------------
// Save hDOM structure to JSON file
// Guardar estructura hDOM a archivo JSON
// ----------------------------------------------------------------------
FUNCTION GuardarAgendaJSON( cNombreArchivo )
LOCAL cJsonTexto := HB_ToJSON( hDOM )
IF !Empty( cJsonTexto )
RETURN hb_MemoWrit( cNombreArchivo, cJsonTexto )
ENDIF
RETURN .F.
// ----------------------------------------------------------------------
// JSON Pure Harbour Serializer / Serializador JSON en Harbour Puro
// ----------------------------------------------------------------------
FUNCTION HB_ToJSON( xVal, nIndent )
LOCAL cType := ValType( xVal )
LOCAL cJson := ""
LOCAL cKey, i, nLen
LOCAL cPad := ""
IF nIndent == Nil
nIndent := 0
ENDIF
cPad := Space( nIndent * 2 )
DO CASE
CASE cType == "H"
cJson := "{" + hb_eol()
nLen := Len( xVal )
i := 0
FOR EACH cKey IN xVal:Keys
i++
cJson += cPad + " " + '"' + hb_ValToStr( cKey ) + '": ' + HB_ToJSON( xVal[ cKey ], nIndent + 1 )
IF i < nLen
cJson += ","
ENDIF
cJson += hb_eol()
NEXT
cJson += cPad + "}"
CASE cType == "A"
cJson := "[" + hb_eol()
nLen := Len( xVal )
FOR i := 1 TO nLen
cJson += cPad + " " + HB_ToJSON( xVal[ i ], nIndent + 1 )
IF i < nLen
cJson += ","
ENDIF
cJson += hb_eol()
NEXT
cJson += cPad + "]"
CASE cType == "C"
cJson := '"' + StrTran( StrTran( xVal, "\", "\\" ), '"', '\"' ) + '"'
CASE cType == "N"
cJson := LTrim( Str( xVal ) )
CASE cType == "L"
cJson := IIF( xVal, "true", "false" )
OTHERWISE
cJson := "null"
ENDCASE
RETURN cJson
// ----------------------------------------------------------------------
// Helper functions for Grid navigation and Indexing
// Funciones auxiliares para la grilla e índices
// ----------------------------------------------------------------------
FUNCTION ValorDaColuna( ControlName, ParentForm, nCol )
LOCAL aRet := {}
IF GetControlType( ControlName, ParentForm ) != "GRID"
RETURN ""
ENDIF
nCol := IIf( nCol == Nil .OR. nCol == 0, 1, nCol )
aRet := GetProperty( ParentForm, ControlName, 'Item', GetProperty( ParentForm, ControlName, 'Value' ) )
IF ValType( aRet ) == "A" .AND. Len( aRet ) >= nCol
RETURN aRet[ nCol ]
ENDIF
RETURN ""
FUNCTION PosicionaIndice( cLetra )
LOCAL i := 0
FOR i := 1 TO 26
IF CHR( i + 64 ) == cLetra
MODIFY CONTROL GIndice OF Form_1 VALUE i
ENDIF
NEXT
Form_1.GIndice.SetFocus
RETURN Nil
FUNCTION Imprimir()
LOCAL cLetra := ValorDaColuna( "GIndice", "Form_1", 1 )
LOCAL cTxt := "=== CONTACTS LIST / LISTA DE CONTACTOS (" + cLetra + ") ===" + hb_eol() + hb_eol()
LOCAL cIdKey, hContacto
IF hb_HHasKey( hDOM, "agenda" ) .AND. hb_HHasKey( hDOM["agenda"], "contactos" )
FOR EACH cIdKey IN hDOM["agenda"]["contactos"]:Keys
hContacto := hDOM["agenda"]["contactos"][ cIdKey ]
IF Upper( Left( hContacto["name"], 1 ) ) == cLetra
cTxt += "ID: " + cIdKey + " | " + hContacto["name"] + " | Tel: " + hContacto["phone"] + hb_eol()
ENDIF
NEXT
ENDIF
MsgInfo( cTxt, "Print Preview / Vista de Impresión" )
RETURN Nil